JVM虚拟机

jvm虚拟机内存模型图

jvm

JVM虚拟机组成

  • 类装载子系统
  • 运行时数据区(内存模型)
  • 字节码执行引擎

运行时数据区(内存模型)

* 栈(线程栈)
    1. 每一个执行线程执行时,会分配一块儿线程专属栈内存区域,用来存储此线程执行过程中用到的局部变量
* 栈帧
    1. 即线程专属栈内存区域中,为不同方法提供的专属内存区域存放此方法的局部变量。(一个方法对应一块儿栈帧内存区域)
    2. 与数据结构中的栈的特点一致:First In,Last Out(先进后出)
    3. 栈帧包括:
        1. 局部变量表
            存放方法的局部变量
        2. 操作数栈
            存放一些临时的操作数的内存区域
            javap -c xx.class 来查看jvm指令码
            操作数栈是为了存放jvm指令码,将指定变量的值装载出来,并在局部变量表中创建一块区域,将指定的(装载出来的)变量放入局部变量表。<插播程序计数器>
        3. 动态链接

        4. 方法出口
            在main方法中调用其它方法时,已经在栈帧内的一块儿内存区域记录了被调方法执行完后要回到main方法的什么位置。
    4. 堆与栈的关系:栈中存放了无数堆中对象的指针(内存地址) 

* 本地方法栈
    1. 本地方法
        native 关键词修饰
        跨源调用一般用本地方法
    2. 本地方法栈则是在调用本地方法时,本地方法需要的一块儿内存区域

* 方法区(共用变量)
    1. 常量
    2. 静态变量(或成员变量)
        当静态变量为对象(new出来),则在方法区中存的是堆中对象的指针(内存地址)
    3. 类信息(比如栈中用到的字节码信息)

* 程序计数器(是每个线程独有的)作用是为了在多线程切换的时候知道从哪个位置继续执行。
    1. 记录当前线程正在运行的那一行jvm指令码的行号位置。(由字节码执行引擎记录)


* 堆  
    -- jvisualvm 调出VisualVM
    1. 年轻代(默认占2/3堆内存)
        1. Eden(伊甸园区)(默认占8/10的年轻代内存)
            存放新new出来的对象  
            - minor gc  
                1. 当新对象占满伊甸园区时,字节码执行引擎后台专门开启的一个线程来执行gc(清理垃圾对象)。
                2. 通过可达性算法找出非垃圾对象后,通过复制算法将对象移到From(此时,这些对象的分代年龄+1),剩余在伊甸园区的垃圾对象一次性清理。
        2. Survivor区  
            From区和To区是minor gc相互交替存放非垃圾对象。
            -  From(默认占8/10 年轻代内存)  
                1. 存放第一次minor gc的复制算法复制过来的非垃圾对象(此时的分代年龄已经+1)
                2. 伊甸园区第二次minor gc的时候,From区也会执行minor gc,此时通过可达性分析算法找出From区中非垃圾对象复制到To(分代年龄+1),然后将From中垃圾对象一次性清理。

            -  To(默认占8/10 年轻代内存)  
                1. 当此区内存在非垃圾对象后,伊甸园区再次执行minor gc时,To区也会执行,只是通过可达性算法找出的将】非垃圾对象复制到From区(分代年龄+1),并清理垃圾对象。

    2. 老年代(默认占2/3堆内存)
        当对象的分代年龄达到15时,会将对象放到老年代。(即一直存活的对象:对象类型的静态变量,线程池内对象,缓存,Spring Bean容器的对象)
        当老年代被放满时会执行full gc
        - full gc
            1. 会尝试的去收集整个堆的垃圾对象
            2. 当老年代收集不出任何垃圾对象,那么下次有对象再复制到老年代时则会OOM(内存溢出)
* GC
    - 可达性分析算法
        将GC Roots对象作为起点,从这些节点开始向下搜索引用对象,找到的对象都标记为非垃圾对象,其余为标记的对象都是垃圾对象
        1. GC Roots:线程栈的本地变量,静态变量,本地方法变量等。
    -  STW(stop the word)
        当进行GC时,会将用户线程暂时停止
    - 对象进入老年代的条件
        1. 大对象直接进入老年代  
            大对象就是需要大量连续内存空间的对象(比如,字符串,数组)。JVM参数:-XX:PretenUreSizeThreshold 可设置大对象的大小,这个参数只在Serial和ParNew两个收集器下有效。  
            比如:-XX:PretenUreSizeThreshold=1000000 -XX:+UseSerialGC  
            原因:为了避免为大对象分配内存时的复制操作而降低效率
        2. 长期存活的对象将进入老年代  
            分代年龄到15
        3. 对象动态分代年龄判断  
            当前放对象的Survivor区里,一批对象的总大小大于这块Survivor区内存大小的50%(-XX:TargetSurvivorRatio 可以设置),那么此时大于等于这批对象年龄最大值的对象可以直接进入老年代。  
            这个规则是希望那些可能是长期存活的对象尽早进入老年代。  
            对象动态年龄判断机制一般在Minor GC 之后出发
        4. Minor GC后存活的对象Survivor区放不下  
            这种情况会把存活的部分对象挪到老年代,部分可能继续在Survivor区
        5. 老年代空间分配担保机制  
            年轻代每次minor gc前JVM都会计算老年代剩余可用空间  
            如果这个可用空间小于年轻代所有对象大小之和(包括垃圾对象)  
            就会看一个(-XX:-HandlePromotionFailTure)是否设置,jdk8默认设置  
            有,则会看老年代可用内存大小会否大于之前每一次minor gc后进入老年代的对象的平均大小,小于或没设置则会出发一次full gc。如果还没有足够空间,则OOM。