- 垃圾收集器的作用:垃圾收集器除了垃圾收集之外,还负责堆的管理与布局、对象分配、与解释器协作、与编译器协作、与监控子系统协作等
Epsilon垃圾收集器
- 特点:不进行垃圾收集,只进行对象的分配。
- 应用场景:如果应用只运行几分钟或者几秒钟,在堆耗尽之前就会退出,那么只需要垃圾收集器能够为对象分配内存即可,不用涉及垃圾收集,这是不进行垃圾回收的Epsilon垃圾收集器很有用。
垃圾收集器的权衡指标
- 应用程序关注点:应用进行数据分析、计算等,需要高吞吐量?应用需要与用交互,需要低延迟?是客户端应用或者嵌入式应用,硬件环境给予的内存不大?
- 硬件:系统建构(x86-32?x86-64?ARM?Aarch64)、处理器数量、分配内存大小、操作系统
- jdk的发行商:OracleJDK?OpenJDk?等
注意:ZGC应用在linux下, 当堆内存4GB-6GB那么CMS处理的比较好,如果堆内存更大,可以选择G 1
内存分配与回收策略实战
新对象通常分配到Eden区,少数情况下(如大对象超过阈值)直接存放到老年代,对象的分配规则取决于垃圾收集器
- 对象优先在Eden分配
如果没有设定大对象直接进入老年代分配的阈值,新生对象都在Eden区分配(不在survivor中分配内存,survivor只用于存储Eden区进行垃圾回收存活的对象的复制),当Eden区满了,那么触发Minor GC。
参数解析:
-Xms20M设定堆的最小值,-Xmx20M设置堆的最小值 ,-Xmn10M设置新生代大小,-XX:PrintGCDetails打印发生垃圾收集时的内存回收日志,以及垃圾回收后当前内存的占用情况(在jdk1.9之后用-XX:Xlog*), -XX:SurvivorRation=8,在新生代中,Eden占80%,两个Survivor占20%,-XX:+HeapDumpOnOutOfMemoryError:堆溢出之后自动生成堆转储快照 - 大对象进入老年代
-XX:PretenureSizeThreshold=3M,指定大于该阈值的新对象(一般大对象为长字符串或者元素很多的数组)直接进入老年代。
这样做的目的:大对象需要连续的内存空间,如果直接进入Eden区,可能明明Eden区还有很多空间(不连续),但会触发Minor GC。同时,可以避免大对象在Eden与Survivor区复制,即尽管Eden区可以放下大对象,但是在垃圾回收时,大对象还是存活的话,被复制到Survivor区,开销很大。
这个参数只堆Serial与ParNew垃圾收集器有用。当垃圾收集器器组合为ParNew + CMS 的时候可以考虑用这个参数调优。 - 长期存活的对象进入老年代
对象每经历一次GC,那么对象头记录的分代年龄就+1,通过参数-XX:MaxTenuringThreshold=15设定当对象的分代年龄超过15时,就进入老年代。该参数默认为15. - 动态对象年龄判定
当Survivor空间中相同年龄的对象大于其空间一半时,那么大于等于该年龄的对象进入老年代,不用等待-XX:MaxTenuringThreshold要求的年龄大小。 - 空间分配担保
在MinorGC之前,先检查老年代最大连续空间大于新生代所有对象的大小,
如果满足,那么MinorGC安全,进行MinorGC。
如果不满足,如果设置了-XX:handlePromotionFailure=true允许担保失败,那么继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,进行MinorGC.否则进行Full GC.
但是在JDk 6 Update24之后,该参数就不起作用了,分配担保的规则为:在Minor之间,检查只要老年代连续可用的空间大于新生代对象总大小或者大于历次晋升的平均大小,就会进行MinorGC否则进行Full GC,让老年代腾出空间。