标签:标准 速度 单线程 标记 时间 current 压缩 compact 一件事
在JVM中,内存是按照分代进行组织的。
其中,堆内存分为年轻代和年老代,非堆内存主要是Permanent区域,主要用于存储一些类的元数据,常量池等信息。而年轻代又分为两种,一种是Eden区域,另外一种是两个大小对等的Survivor区域。之所以将Java内存按照分代进行组织,主要是基于这样一个“弱假设” - 大多数对象都在年轻时候死亡。同时,将内存按照分代进行组织,使得我们可以在不同的分代上使用不同的垃圾回收算法,使得整个内存的垃圾回收更加有效。
在年轻代上采用的垃圾回收算法是“Mark-Copy”算法,并不同于我们前面所了解的任何一种基本垃圾回收算法,但是Mark算法是一样的,基于根对象找到所有的可达对象,具体可看Mark-Sweep算法中的Mark步骤. 而对于Copy算法,它仅仅是简单的将符合一定年龄的对象从一个分代拷贝到另一个分代。具体的回收过程如下:
首先,新对象的内存分配都是先在Eden区域中进行的,当Eden区域的空间不足于分配新对象时,就会触发年轻代上的垃圾回收(发生在Eden和Survivor内存区域上),我们称之为"minor garbage collection".同时,每个对象都有一个“年龄”,这个年龄实际上指的就是该对象经历过的minor gc的次数。如图1所示,当对象刚分配到Eden区域时,对象的年龄为“0”,当minor gc被触发后,所有存活的对象(仍然可达对象)会被拷贝到其中一个Survivor区域,同时年龄增长为“1”。并清除整个Eden内存区域中的非可达对象。
当第二次minor gc被触发时(如图2所示),JVM会通过Mark算法找出所有在Eden内存区域和Survivor1内存区域存活的对象,并将他们拷贝到新的Survivor2内存区域(这也就是为什么需要两个大小一样的Survivor区域的原因),同时对象的年龄加1. 最后,清除所有在Eden内存区域和Survivor1内存区域的非可达对象。
当对象的年龄足够大(这个年龄可以通过JVM参数进行指定,这里假定是2),当minor gc再次发生时,它会从Survivor内存区域中升级到年老代中,如图3所示。
其实,即使对象的年龄不够大,但是Survivor内存区域中没有足够的空间来容纳从Eden升级过来的对象时,也会有部分对象直接升级到Tenured内存区域中。
当minor gc发生时,又有对象从Survivor区域升级到Tenured区域,但是Tenured区域已经没有空间容纳新的对象了,那么这个时候就会触发年老代上的垃圾回收,我们称之为"major garbage collection".
而在年老代上选择的垃圾回收算法则取决于JVM上采用的是什么垃圾回收器。通过的垃圾回收器有两种:Parallel Scavenge(PS) 和Concurrent Mark Sweep(CMS)。这两种垃圾回收器的不同更多的是体现在年老代的垃圾回收过程中,年轻代的垃圾回收过程在这两种垃圾回收器中基本上是一致的。
就像其名字所表示的那样,Parallel Scavenge垃圾回收器在执行垃圾回收时使用了多线程来一起进行垃圾回收,这样可以提高垃圾回收的效率。而Concurrent Mark Sweep垃圾回收器在进行垃圾回收时,应用程序可以同时运行。
PS垃圾回收器在年老代上采用的垃圾回收算法可以看作是标记-清除算法和标记-压缩算法的结合体。
首先,PS垃圾回收器先是会在年老代上使用标记-清除算法来回收掉非可达对象所占有的空间,但是我们知道,标记清除算法的一个缺陷就是它会引起内存碎片问题。继而有可能会引发连续的major gc。假设当前存在的内存碎片有10M,但最大的内存碎片只能容纳2M的对象,这个时候如果有一个3M的对象从Survivor区域升级到Tenured区域,那Tenured区域也没有办法存放这个3M的对象。结果就是不断的触发major gc,直到Out of Memory。所以,PS垃圾回收器在清除非可达对象后,还会进行一次compact,来消除内存碎片。
CMS垃圾收集器相比于PS垃圾收集器,它成功的减少了垃圾收集时暂停应用程序的时间,因为CMS在进行垃圾收集时,应用程序是可以并行运行的。下面让我们来看看它是怎么做到的。
从它的名字可以看出,CMS垃圾收集器在年老代上采用的垃圾回收算法是标记-清除算法。但是,它跟标准的标记-清除算法略有不同。它主要分为四个阶段:
从下图可以看到PS和CMS垃圾收集器的区别:
黑色箭头代表应用程序的运行,绿色箭头代表CMS垃圾收集器的运行。一根线条表示单线程,多个线条表示多线程。
所以,相比于PS垃圾收集器,CMS垃圾收集器成功的减少了应用程序暂时的时间。
但是很不幸的是,CMS垃圾收集器虽然减少了暂停应用程序的运行时间,但是由于它没有Compact阶段,它还是存在着内存碎片问题。于是,为了去除内存碎片问题,同时又保留CMS垃圾收集器低暂停时间的优点,JAVA7发布了一个新的垃圾收集器 - G1垃圾收集器。它会在未来逐步替换掉CMS垃圾收集器。
G1垃圾收集器和CMS垃圾收集器有几点不同。首先,最大的不同是内存的组织方式变了。Eden,Survivor和Tenured等内存区域不再是连续的了,而是变成了一个个大小一样的region - 每个region从1M到32M不等。
一个region有可能属于Eden,Survivor或者Tenured内存区域。图中的E表示该region属于Eden内存区域,S表示属于Survivor内存区域,T表示属于Tenured内存区域。图中空白的表示未使用的内存空间。G1垃圾收集器还增加了一种新的内存区域,叫做Humongous内存区域,如图中的H块。这种内存区域主要用于存储大对象-即大小超过一个region大小的50%的对象。
在G1垃圾收集器中,年轻代的垃圾回收过程跟PS垃圾收集器和CMS垃圾收集器差不多,新对象的分配还是在Eden region中,当所有Eden region的大小超过某个值时,触发minor gc,回收Eden region和Survivor region上的非可达对象,同时升级存活的可达对象到对应的Survivor region和Tenured region上。对象从Survivor region升级到Tenured region依然是取决于对象的年龄。
对于年老代上的垃圾收集,G1垃圾收集器也分为4个阶段,基本跟CMS垃圾收集器一样,但略有不同:
从上可以看到,由于Initial Mark阶段和Clean up/Copy阶段都是跟minor gc同时发生的,相比于CMS,G1暂停应用程序的时间更少,从而提高了垃圾回收的效率。
摘自:http://www.jianshu.com/p/778dd3848196
标签:标准 速度 单线程 标记 时间 current 压缩 compact 一件事
原文地址:http://www.cnblogs.com/andriy-h/p/6686831.html