注:此处的JVM特指HOTSPOT虚拟机
名词解释:
GC:垃圾收集器
Minor GC:新生代GC, 指的是发生在新生代的垃圾收集动作,
Major GC/FULL GC: 发生在老年代的GC动作
JVM中堆空间分代
JVM中将堆空间分为新生代和老年代,新生代又被分为1个Eden区和两个Survivor区(ToSpace和FromSpace)。
为什么要分代?
分代的唯一理由是优化GC性能,因为java中80%的对象都是朝生夕死的,如果没有分代,GC时将会对堆的所有区域进行扫描,浪费性能。
堆空间分代资源分配策略
-XX:+PrintGCDetails 通过这个收集器日志参数,可以告诉虚拟机在发生垃圾收集行为时打印内存回收日志,并且在进程退出的时候输出当前的内存各区域分配情况。
-Xms20M 表示java虚拟机初始内存为20M
-Xmx100M表示java虚拟机最大可用内存为100M
-Xmn10M表示设置年轻代的可用内存为10M,一般情况下扩大了年轻代内存,相应的会缩小老年代内存
-XX:SurvivorRatio=8决定了新生代中Eden区与一个Survivor区的空间比例是8:1
垃圾回收策略:
大多数情况下,新创建的对象都会被放在Eden区,当Eden区空间不足时,会触发Minor GC(一般采用复制算法),在GC开始的时候,对象只会存在于Eden区和From区,To区是空的,紧接着进行GC,Eden区的所有存活对象都会被复制到To区,而在From区,则会根据存活对象的年龄来决定去向,当年龄达到阀值(通过 -XX:MaxTenuringThreshold来设置)的存活对象会被移动到老年代中,没有达到阀值的存活对象会被移动到To区。经过本次GC,Eden区和From区已经被清空。此时To区和From区会交换他们的角色,即To区变成下次GC的From区,From区变为下次GC的To区。
动态对象年龄分代:
为了更好地适应不同程序的内存状况,虚拟机并不是永远要求对象的年龄必须达到MaxTenuringThreshold才能晋升老年代,如果Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无需等到MaxtenuringThreshold中要求的年龄。
空间分配担保
在新生代使用复制收集算法,一般只使用一个survivor空间作为轮换备份,因此在出现大量对象在Minor Gc后仍然存活的情况,就需要老年代进行分配担保,把Survivor无法容纳的对象直接进入老年代,而老年代要进行这样的担保的前提是自身需要具备容纳这些对象的剩余空间,因此在这种情况下会检查老年代的最大连续可用空间大小是否要大于新生代所有对象总空间,如果条件成立,才能保证Minor GC是安全的。如果不成立,则需要查看HandlePromotionFailure来设置是否允许担保失败,如果允许,那么会继续检查老年代最大可用连续空间是否大于历次晋升到老年代的对象的平均大小,如果大于,将尝试进行MinorGc,如果小于或者HandlePromotionFailure设置不允许冒险,则改为进行一次Full GC。
新生代GC(Minor GC):指发生在新生代的垃圾收集行为,因为Java对象大多都具备朝生夕灭的特性,所以MinorGc非常频繁,一般回收速度也比较快。
老年代GC(Major GC):指发生在老年代的GC,出现了MajorGC一般会伴随着至少一次Minor GC(并非绝对),Major GC速度一般比Minor GC慢10倍以上。