内存分配策略与回收策略
参考:《深入理解Java虚拟机》-jvm高级特性与最佳实现(周志明著)
前言
Java技术体系所倡导的内存管理最终可以贵大为自动化解决两个问题:给对象分配内存以及回收分配给对象的内存。
如下的内容是基于Client模式下默认的垃圾收集器组合
一、内存分配的普遍规则
1、对象优先在Eden空间分配,大多情况下,对象在新生代的Eden空间进行分配,当Eden空间没有足够的内存时,将触发一次Minor GC,可以通过设置-XX:+PrintGCDetails 这个收集器日志参数,高速虚拟机在发生垃圾回收的时候打印GC日志。
注意:
Minor GC 和 Full GC 有什么不一样吗?
新生代GC(Minor GC):是指新生代的垃圾收集动作,因为Java对象大多朝生夕灭,所以Minor GC发生的非常频繁。一般回收速度也比较快。
老年代GC(Major GC / Full GC):Major GC清理Tenured区,用于回收老年代,出现Major GC通常会出现至少一次Minor GC。
Full GC是针对整个新生代、老生代、元空间(metaspace,java8以上版本取代perm gen)的全局范围的GC。Full GC不等于Major GC,也不等于Minor GC+Major GC,发生Full GC需要看使用了什么垃圾收集器组合,才能解释是什么样的垃圾回收。
2、大对象直接进入老年代,最典型的大对象就算是指那种很长的字符串以及数组。虚拟机提供了-XX:PretenureSizeThreshold 参数,令大于这个设置值的对象直接在老年代分配。这样做的目的是避免在Eden空间以及两个Survivor区之间发生大量的 内存复制。
需要注意的是,-XX:PretenureSizeThreshold 这个参数对Parallel Scavenge 收集器不起作用,如果遇到必须使用该设置的场合,可以考虑使用ParNew 加 CMS 收集器组合
3、长期存活的对象将进入老年代
虚拟机给每个对象都定义了一个年龄计数器,如果对象在Eden出生,并经过第一次Minor GC 后存活,并且被Survivor容纳的话,将被移动到Survivor空间中,并且对象年龄设置为1,对象在Survivor区中每熬过一次Minor GC,年龄就增加1岁,当它的年龄增加超过默认(15岁)的时候,会被晋升到老年代中,对象晋升老年代的年龄阈值,可以通过设置参数 -XX:MaxTenuringThreshold设置
4、动态对象年龄判断
为了更好地适应不同程序的内存情况,虚拟机并不是永远地要求对象的年龄必须达到MaxTenuringThreshold 才能晋升老年代。如果再Survivor空间中相同年龄的所有对象大小的总和超过了Survivor空间的一半,年龄大于等于该年龄的对象就可以直接进入到老年代。
5、空间分配担保
在发生Minor GC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象的总和。如果这个条件成立,那么Minor GC 可以确保实施安全的。如果不成立,则虚拟机会查看HandlerPromotionFailure设置是否允许担保失败。如果允许,那么就会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试一次Minor GC,尽管这次Minor GC 是有风险的。如果小于或者HandlerPromotionFailure设置的值不允许冒险,那这时也要改为进行一次Full GC