JAVA堆主要分成两个区域,新生代和老年代。

新生代:Eden 标记-清除(内存碎片多) 新生代:S0,S1 标记-复制(无碎片,但内存空间消耗大) 老年代:标记-整理(没有内存碎片,但移动对象有开销)
新生代:老年代-> 1:2 新生代 Eden:S0:S1 -> 8:1:1 (大部分对象都会被回收,所以S0和S1的空间尽量小,最大限度地降低了复制算法造成的对象频繁拷贝带来的开销。)
CG的流程:
- 对象首先创建在Eden区。
- 当Eden区满了,触发MinorGC,经过Minor GC 后,98%的对象会被回收,只有少部分对象会存活,它们会被移到S0区。
- 当触发下一次Minor GC 时,会把 Eden 区的存活对象和 S0(或S1)中的存活对象(S0 或 S1 中的存活对象经过每次 Minor GC 都可能被回收)一起移到S1(Ede和S0 的存活对象年龄+1), 同时清空Eden和S0的空间。
- 对象如果存货,就在S0和S1中间来回移动,当达到对象的年龄阈值之后,会被移到老年代,如果是大对象,也直接进入老年代。
- 如果老年代也满了, 将触发Full GC, Full GC 会同时回收新生代和老年代(即对整个堆进行GC),它会导致 Stop The World(简称 STW),造成挺大的性能开销。
新生代设置成 Eden, S0,S1区或者给对象设置年龄阈值或者默认把新生代与老年代的空间大小设置成 1:2 都是为了尽可能地避免对象过早地进入老年代,尽可能晚地触发 Full GC。
常用垃圾收集器种类
新生代收集器:Serial、ParNew、Parallel Scavenge
老年代收集器:CMS、Serial Old、Parallel Old
整堆收集器: G1

ParNew: 属于Serial回收器的多线程版本,采用复制算法。运行在新生代区域
Parallel Scavenge(多线程): 运行在新生代区域,属于多线程的回收器,采用复制算法。 与ParNew不同的是,ParNew回收器是通过控制垃圾回收的线程数来进行参数调整,而Parallel Scavenge回收器更关心的是程序运行的吞吐量
CMS(Concurrent Mark Sweep) 收集器 以最短回收停顿时间为目标的收集器,CMS 收集器垃圾收集可以看做是和用户线程并发执行的,就像一边扔垃圾一边收集垃圾。
- 备注:因为CMS 是基于标记-清除算法,所以垃圾回收后会产生空间碎片,可以通过 -XX:UserCMSCompactAtFullCollection 开启碎片整理(默认开启),在 CMS 进行 Full GC 之前,会进行内存碎片的整理。还可以用 -XX:CMSFullGCsBeforeCompaction 设置执行多少次不压缩(不进行碎片整理)的 Full GC 之后,跟着来一次带压缩(碎片整理)的 Full GC。 适用场景:重视服务器响应速度,要求系统停顿时间最短。可以使用 -XX:+UserConMarkSweepGC 来选择 CMS 作为老年代收集器。
Parallel Old 收集器: 是 Parallel Scavenge 的老年代版本,是一个多线程收集器,采用标记-整理算法。可以与 Parallel Scavenge 收集器搭配,可以充分利用多核 CPU 的计算能力。 适用场景:与Parallel Scavenge 收集器搭配使用;注重吞吐量。jdk7、jdk8 默认使用该收集器作为老年代收集器,使用 -XX:+UseParallelOldGC 来指定使用 Paralle Old 收集器。
最后附上GC参数的整理:

重要心得:没有问题不需要调优,大部分情况下默认的组合是最佳的,如果程序的GC时间随着压测的时间,变得越来越慢的时候(比如压24小时),首先考虑自己的程序代码是否有写法上的问题,是否符合编码规范,或者对变量的使用是否没有经过慎重考虑,业务逻辑上的疏漏导致的,这种情况通过调优是无法改进的。