JVM垃圾收集总结

垃圾收集器种类

新生代垃圾收集器,新生代的垃圾收集器都采用复制算法:

1,Serial:很古老的垃圾收集器,是单线程执行的,可以配合Serial Old或者CMS使用

2,ParNew:Serial的多线程版本,可以配合Serial Old或者配合CMS使用

3, Parallel Scavenge:关注点在系统的吞吐量,使得系统的吞吐量尽量在一个可控的范围内,可以配合Serial Old或者Parallel Old使用

老年代垃圾收集器:

1,Serial Old:Serial的老年代版本,同样是一个单线程的垃圾收集器,使用标记-整理算法

2,Parallel Old:Parallel Scavenge收集器的老年代版本,使用标记-整理算法。

3,CMS(Concurrent Mark Sweep):基于标记-清除(不适用整理我觉得是因为清除阶段用户线程也在运行,也在创建新的对象,所以无法将存活下来的对象都移动到一端)算法实现,CMS的设计目标是希望用户停顿的时间最少。CMS分为以下四个阶段:

(1)初始标记,标记GC Roots对象能关联到的第一级对象,此阶段会产生STW(stop to word),但停顿时间非常短。

(2)并发标记,标记所有可达对象,这个阶段GC线程和用户线程并发执行,同时这个阶段用户线程可能会创建新的对象。

(3)重新标记,由于并发标记阶段创建了一些对象,所以重新标记阶段需要判断并发标记阶段创建的对象是否需要被重新标记为可达,此阶段会产生STW

(4)并发清除,清除前面未被标记(不可达)的对象,这个阶段GC线程与用户线程并发执行。

另外还有一个G1收集器,可以同时回收新生代和老年代的对象。

以下是各个垃圾收集器之间的组合使用关系

JVM垃圾收集总结_第1张图片

垃圾回收算法

标记-清除算法

按照可达性分析算法,标记可达的对象,然后将未标记的对象清除。这种算法有两个缺点,一是会造成内存碎片,二清除阶段会遍历所有对象,效率比较低。

复制算法

复制算法一般采用两个区域,每次只使用一个区域,每次进行垃圾回收的时候,都将一个区域中的可达对象复制到另一个区域中(不用标记,直接用可达性算法搜索可达对象然后复制),然后将原来区域中的所有对象清除。这种算法速度很快,特别是可达对象比较少的时候(可达对象少的话,可达性分析算法能迅速遍历完所有对象)。因为新生代对象每次回收的时候大部分对象都已经死亡(IBM公司研究新生代回收时98%对象都是已经死亡的),只有少数可达对象,所以复制算法特别适合新生代对象的回收。但是复制算法因为同时只使用两块区域中的一块区域,另一块区域空闲着,导致空间利用率降低。所以HotSpot的新生代的复制算法并没有使用两块区域,而是使用三块区域,一个Eden区,两个Suvivor区域,Eden区域和Suvivor区域的占比默认为8 :1,创建对象的时候只使用Eden区域,之后回收的时候,将Eden中的可达对象复制到一个Suvivor区域,下次再回收的时候,将Eden和Suvivor中的可达对象复制到另一个Suvivor区域,每次复制一个Suvivor中的对象到另一个Suvivor区域,对象的年龄都要加1,当到达一定年龄之后,对象就进入老年代。为什么Eden和Suvivor占比为8 :1呢?因为对象都是朝生夕死的,每次GC之后,只有少数对象能够存活,所以Suvivor区域不需要很大,能够存放存活下来的对象行了。

标记-整理算法

标记-整理算法的标记阶段和标记-清除算法的标记阶段一样,区别是标记之后先不清除,而是先将对象都移动到一端,然后清理掉端边界以外的内存。这样的好处是不会产生内存碎片。

一般来说,新生代都采用复制算法回收对象,老年代采用标记-整理或者标记-清除算法收集对象,标记-清除或整理算法不适合回收新生代对象,一是因为新生代对象回收频率比较高,而且大部分对象都是朝生夕死,倘若每次都遍历所有对象,就太耗费时间了。老年代不适合使用复制算法,因为老年代的对象比较多,而且每次回收之后大部分对象都是存活的,使用复制算法的话会极大的浪费空间。

你可能感兴趣的:(JVM)