Java分代垃圾回收策略原理详解

  

<强>一,为什么要分代

  

分代的垃圾回收策略,是基于这样一个事实:不同的对象的生命周期是不一样的,因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率。

  

在Java程序运行的过程中,会产生大量的对象,其中有些对象是与业务信息相关,比如Http请求中会话的对象,线程,插座连接,这类对象跟业务直接挂钩,因此生命周期比较长。但是还有一些对象,主要是程序运行过程中生成的临时变量,这些对象生命周期会比较短,比如:字符串对象,由于其不变类的特性,系统会产生大量的这些对象,有些对象甚至只用一次即可回收。

  

试想,在不进行对象存活时间区分的情况下,每次垃圾回收都是对整个堆空间进行回收,花费时间相对会长,同时,因为每次回收都需要遍历所有存活对象,但实际上,对于生命周期长的对象而言,这种遍历是没有效果的,因为可能进行了很多次遍历,但是他们依旧存在。因此,分代垃圾回收采用分治的思想,进行代的划分,把不同生命周期的对象放在不同代上,不同代上采用最适合它的垃圾回收方式进行回收。

  

<强>二,如何分代

  

如图所示:

  

癑ava分代垃圾回收策略原理详解"

  

虚拟机中的共划分为三个代:年轻代(年轻一代),年老代(旧一代)和持久代(永久代)。其中持久代主要存放的是Java类的类信息,与垃圾收集要收集的Java对象关系不大。年轻代和年老代的划分是对垃圾收集影响比较大的。

  

年轻代:

  

所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。年轻代分三个区,一个伊甸园区,两个幸存者区(一般而言)。大部分对象在伊甸园区中生成。当伊甸园区满时,还存活的对象将被复制到幸存者区(两个中的一个),当这个幸存者区满时,此区的存活对象将被复制到另外一个幸存者区,当这个幸存者区也满了的时候,从第一个幸存者区复制过来的并且此时还存活的对象,将被复制”年老区(终身)”。需要注意,幸存者的两个区是对称的,没先后关系,所以同一个区中可能同时存在从伊甸园复制过来的对象,和从前一个幸存者复制过来的对象,而复制到年老区的只有从第一个幸存者区过来的对象,而且,幸存者区总有一个是空的。同时,根据程序需要,幸存者区是可以配置为多个的(多于两个),这样可以增加对象在年轻代中的存在时间,减少被放到年老代的可能。

  

新生代有划分为伊甸园,从幸存者和幸存者三个部分,他们对应的内存空间的大小比例为8:1:1,也就是,为对象分配内存的时候,首先使用伊甸园空间,经GC后过,没有被回收的会首先进入从幸存者区域,任何时候,都会保持一个Survivorq区域(从幸存者或幸存者)完全空闲,也就是说新生代的内存利用率最大为90% .从幸存者和幸存者两个区域会根据GC的实际情况,进行互换,将从幸存者区域中的对象全部复制到到幸存者区域中,或者反过来,将对幸存者区域中的对象全部复制到从幸存者区域中。

  

年老代:

  

在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中,因此,可以认为年老代中存放的都是一些生命周期较长的对象。

  

GC过程中,当某些对象经过多次GC都没有被回收,可能会进入到年老代,或者,当新生代没有足够的空间来为对象分配内存时,可能会直接在年老代进行分配。

  

持久代:

  

用于存放静态文件,如今Java类,方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些类,例如Hibernate等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过- xx: MaxPermSize=& lt; N>进行设置。

  

永久代实际上对应着虚拟机运行时数据区的“方法区”,这里主要存放类信息,静态变量,常量等数据。一般情况下,永久代中对应的对象的GC效率非常低,因为这里的的大部分对象在运行都不要进行GC,它们会一直被利用,直退到JVM出。

  

<强>三,什么情况下触发垃圾回收

  

由于对象进行了分代处理,因此垃圾回收区域,时间也不一样.GC有两种类型:清除GC和GC。

  

清除GC   

一般情况下,当新对象生成,并且在伊甸园申请空间失败时,就会触发清除GC,对伊甸园区域进行GC,清除非存活对象,并且把尚且存活的对象移动区到幸存者。然后整理幸存者的两个区。这种方式的GC是对年轻代的伊甸园区进行,不会影响到年老代。因为大部分对象都是从伊甸园区开始的,同时伊甸园区不会分配的很大,所以伊甸园区的GC会频繁进行。因而,一般在这里需要使用速度快,效率高的算法,使伊甸园去能尽快空闲出来。

Java分代垃圾回收策略原理详解