来自Oracle官方文档,对JVM GC知识整理的清晰易懂,查资料还是看官方的好!
1 GC步骤简述
步骤1:标记 (Marking)
根据对象引用关系,将未被任何对象引用的对象实例标记出来,如下图中的黄色。当堆中的所有对象都要被扫描一遍时(如Major GC),将会非常耗时。
步骤2:删除 (Sweeping)
? 普通删除:直接删除未被引用的对象,之后内存分配器(memory allocator)要持有指向空闲内存的指针,以便后续内存分配。
? 压缩删除:为了进一步提高性能并简化内存分配器的设计,可以在删除后将剩余的存活对象移动到一起。后面讲到的对Old Generation进行回收使用的就是这种方式。
注:Young Generation使用的是另一种拷贝删除回收,而不是这两种原地进行的回收。
2 GC中的堆
堆分为不同的代(Generation),Young Generation,包括eden,S0和S1,保存所有新建的对象。Old Generation保存多次GC后存活的对象,具体代数可以配置。而Permanent Generation保存class和method字节码,只在Major GC时回收。
注:印象里Perm Space的回收条件非常苛刻,类的所有实例都已不被引用,类的ClassLoader不被引用等条件,所以一般回收率非常低。
下面说一下在GC过程中这些Generation是怎样使用的:
? 首先,当eden分配满时会触发Minor GC,会与S0或S1中的一个(作为From Space)开始回收,将剩余对象拷贝到另一个(作为To Space),Minor GC也会导致应用程序的所有线程停顿(Stop the World)。
? 之后,每经过一次Minor GC后存活的对象的generation数都加1,逐渐老化(aging),达到一定值时会升级移动到tenured space。
? 最后,当tenured space达到一定使用率时,会触发Major GC,对整个堆(包括Perm)进行Stop the World式的回收。
3 观察GC过程
使用jstat命令就能亲自观察到上述的GC过程,用法为jstat -gcutil pid interval。如下图中的例子。
4 常用垃圾回收器
? -XX:+UseSerialGC:串行回收,适应于对响应时间要求不高的应用,或JVM实例数远多于CPU数的环境。
? -XX:+UseParallelGC -XX:+UseParallelOldGC:多线程回收Young,Old Generation。用于可以接受长时间停顿而换取高吞吐量的场景,如生成报表或执行慢数据库查询的批处理任务。
注:因为吞吐量关注如何最大化应用在一段时间内完成的总任务量,通常是比较长的一段时间,所以能接受长停顿时间,通常不考虑响应速度的问题。
? -XX:+UseConcMarkSweepGC:CMS尝试最小化停顿时间,所以一般不会执行压缩(有选项可以强制开启),如果碎片会导致问题,那就分配更大的堆。CMS一般用于要求低停顿时间的应用,例如桌面应用或web服务器应用。
? -XX:+UseG1GC:G1回收器是并行、增量的,用来替代CMS。但目前仍处于实验阶段,如果使用CMS已经有不错的性能就无需尝试更换G1。