jvm中垃圾回收的算法有许多,这篇主要介绍generration算法
一.原理
首先每个对象的生存周期是不同的,所以generation算法将不同对象采用不同的回收策略。
年轻代:年轻代就是为了快速清理掉那些生存周期短的对象而设立的,年轻代分为三个模块,一个eden区,两个survivor区(survivor0和survivor1),它们内存按8:1:1分配,一个新的对象建立首先在eden区,年轻代的回收叫做minor GC,在回收时将eden区中存活的对象复制到survivor0区中,让后清空eden区。当survivor0区的内存被存满时,eden区和survivor0区将全部存活的对象存入survivor1区。然后将现在的survivor0区和eden区清空,之后现在的survivor1区与survivor0区交换,后队变前队一直循环,直到回收时当时的survivor1区存不下当时的survivor0+eden区的存活对象时就将存活对象放入老年代。
老年代:当对象在年轻代经历过次次历练后,他终于存活到了老年区,所以老年代中的对象大多都是一些生命周期比较长的对象,老年代也比年轻代分到到的内存要大,默认是1:2。当老年代内存也存满时就会触发一次full GC或者叫major GC也就是对年轻代老年代都进行回收。
持久代:用于存放静态文件,如Java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate 等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。
GC的执行机制
由于对象进行了分代处理,因此垃圾回收区域、时间也不一样。GC有两种类型:Scavenge GC和Full GC。
Scavenge GC
一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲出来。
Full GC
对整个堆进行整理,包括Young、Tenured和Perm。Full GC因为需要对整个对进行回收,所以比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于FullGC的调节。有如下原因可能导致Full GC:
1.年老代(Tenured)被写满
2.持久代(Perm)被写满
3.System.gc()被显示调用
4.上一次GC之后Heap的各域分配策略动态变化
GC收集器:
新生代收集器使用的收集器:Serial、PraNew、Parallel Scavenge
老年代收集器使用的收集器:Serial Old、Parallel Old、CMS
Serial收集器(复制算法)
新生代单线程收集器,标记和清理都是单线程,优点是简单高效。
Serial Old收集器(标记-整理算法)
老年代单线程收集器,Serial收集器的老年代版本。
ParNew收集器(停止-复制算法)
新生代收集器,可以认为是Serial收集器的多线程版本,在多核CPU环境下有着比Serial更好的表现。
Parallel Scavenge收集器(停止-复制算法)
并行收集器,追求高吞吐量,高效利用CPU。吞吐量一般为99%, 吞吐量= 用户线程时间/(用户线程时间+GC线程时间)。适合后台应用等对交互相应要求不高的场景。
Parallel Old收集器(停止-复制算法)
Parallel Scavenge收集器的老年代版本,并行收集器,吞吐量优先
CMS(Concurrent Mark Sweep)收集器(标记-清理算法)
高并发、低停顿,追求最短GC回收停顿时间,cpu占用比较高,响应时间快,停顿时间短,多核cpu 追求高响应时间的选择