JVM垃圾回收

Posted 小~虎

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM垃圾回收相关的知识,希望对你有一定的参考价值。

 垃圾回收的概念和其算法

垃圾回收(Garbage Collection,简称GC),GC中的垃圾特指存于内存中,不会再被使用的对象,而回收就是相当于把垃圾“倒掉”。垃圾回收有很多算法,如引用计数法,标记压缩法,复制算法,分代、分区的思想。

 

引用计数法:这是个比较古老而经典的垃圾收集算法,其核心就是在对象被其他所引用时计数器加1,而当引用失效时则减1,但是这种方式有非常严重的问题:无法处理循环引用的情况、还有就是每次进行加减操作比较浪费系统性能。

标记清除法;就是分为标记和清楚两个阶段进行处理内存中的对象,当然这种方式也有非常大的弊端,就是空间碎片问题,垃圾回收后的空间不是连续的,不连续的内存空间的工作效率要低于连续的内存空间。

复制算法:其核心思想就是将内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存留对象复制到未被使用的内存块中去,之后去清楚之前正在使用的内存块中所有的对象,反复去交换两个内存的角色,完成垃圾收集。(java中新生代的from和to空间就是使用这个算法)

标记压缩法:标记压缩法在标记清除法基础之上做了优化,把存活的对象压缩到内存一端,而后进行垃圾清理。(java中老年代使用的就是标记压缩法)

分代算法:就是根据对象的特点把内存分为N块,而后根据每个内存的特点使用不同的算法。

对于新生代和老年代来说,新生代回收频率很高,但是每次回收耗时都很短,而老年代回收频率较低,但是耗时会相对较长,所以应该尽量减少老年代的GC。

分区算法:其主要就是将整个内存分为N多个小的独立空间,每个小空间都可以独立使用,这样细粒度的控制一次回收都少个小空间和那些个小空间,而不是对整个空间进行GC,从而提升性能,并减少GC的停顿时间,

垃圾回收时的停顿现象

垃圾回收器的任务是识别和回收垃圾对象进行内存清理,为了让垃圾回收器可以高效的执行,大部分情况下,会要求系统进入一个停顿的状态。停顿的目的是终止所有应用线程,只有这样系统才不会有新的垃圾产生,同时停顿保证了系统状态在某一瞬间的一致性,也有益于更好的标记垃圾对象。因此在垃圾回收时,都会产生应用程序的停顿。

对象如何进入老年代

一般而言,对象首次创建会被放置在新生代的eden区,如果没有GC介入,则对象不会离开eden区,那么eden区的对象如何进入老年代呢?一般来讲,只要对象的年龄达到一定的大小,就会自动离开年轻代进入老年代,对象年龄是由对象经历数次GC来决定的,在新生代每次GC之后如果对象没有被回收则年龄+1,虚拟机提供了一个参数来控制新生代对象的最大年龄,当超过这个年龄范围就会晋升老年代。

-XX:MaxTenuringThreshold,默认情况下为15

总结:根据设置MaxTenuringThreshold参数,可以指定新生代对象经过多少次回收后进入老年代。

另外,大对象(新生代eden区无法装入时,也会直接进入老年代)。JVM里有个参数可以设置对象的大小超过在指定的大小之后,直接晋升老年代。

-XX:PretenureSizeThreshold

总结:使用PretenureSizeThreshold可以进行指定进入老年代的对象大小,但是要注意TLAB区域优先分配空间。

TLAB

TLAB全称是Thread Local Allocation Buffer 即线程本地分配缓存,从名字上看是一个线程专用的内存分配区域,是为了加速对象分配而生的。每一个线程都会产生一个TLAB,该线程独享的工作区域,java虚拟机使用这种TLAB区来避免多线程冲突问题,提高了对象分配的效率。TLAB空间一般不会太大,当大对象无法在TLAB分配时,则会直接分配到堆上。

-XX:+UseTLAB 使用TLAB

-XX:+TLABSize 设置TLAB大小

-XX:TLABRefillWasteFraction 设置维护进入TLAB空间的单个对象大小,他是一个比例值,默认为64,即如果对象大于整个空间的1/64,则在堆创建对象。

-XX:+PrintTLAB 查看TLAB信息

-XX:ResizeTLAB 自调整TLABRefillWasteFraction阀值

对象创建流程图

一个对象创建在什么位置,JVM会有一个比较细节的流程,根据数据的大小,参数的设置,决定如何创建分配,以及其位置,

垃圾收集器

 在java虚拟机中,垃圾回收器不仅仅只有一种,什么情况下该使用哪种,对性能又有什么样的影响。

串行垃圾回收器

并行垃圾回收器

CMS回收器

G1回收器

串行回收器

串行回收器是指使用单线程进行垃圾回收的回收器。每次回收时,串行回收器只有一个工作线程,对于并行能力较弱的计算机来说,串行回收器的专注性和独占性往往有更好的性能表现。串行回收器可以在新生代和老年代使用,根据作用于不同的堆空间,分为心神大概串行回收器和老年代串行回收器。

使用-XX:+UseSerialGC参数可以设置使用新生代串行回收器和老年代串行回收器

并行回收器(ParNew回收器)

并行回收器在串行回收器基础上做了改进,他可以使用多个线程同时进行垃圾回收,对于计算能力强的计算机而言,可以有效的缩短垃圾回收所需的实际时间。

ParNew回收器是一个工作在新生代的垃圾收集器,他只是简单的将串行回收器多线程化,他的回收策略和算法和串行回收器一样。

使用-XX:+UseParNewGC 新生代ParNew回收器,老年代则使用串行回收器

ParNew回收器工作时的线程数量可以使用-XX:ParallelGCThreads参数指定,一般最好和计算机的CPU相当,避免过多的线程影响性能

并行回收器(ParallelGC回收器)

新生代ParallelGC回收器,使用了复制算法的收集器,也是多线程独占形式的收集器,但ParallelGC回收器有个非常重要的特点,就是它非常关注系统的吞吐量。

提供了两个非常关键的参数控制系统的吞吐量

-XX:MaxGCPauseMillis :设置对打垃圾收集停顿时间,可用把虚拟机在GC停顿的时间控制在MaxGCPauseMillis范围内,如果希望减少GC停顿时间可以将MaxGCPauseMillis设置的很小,但是会导致GC频繁,从而增加了GC的总时间,降低了吞吐量。所以需要根据实际情况设置该值。

-XX:GCTimeRatio:设置吞吐量大小,他是一个0到100之间的整数,默认情况下他的取值是99,那么系统将花费不超过1/(1+n)的时间用于垃圾回收。也就是1/(1+99)=1%的时间。

还可以指定-XX:+UseAdaptiveSizePolicy打开自适应模式,在这种模式下,新生代的大小,eden、from/to的比例,以及晋升老年代的对象年龄参数会被自动调整,以达到在堆大小,吞吐量和停顿时间之间的平衡点。

CMS回收器

CMS全称为:ConcurrentMark-Sweep 并发标记清除,他使用的是标记清除法,主要关注系统停顿时间。

使用-XX:+UseDondMarkSweepGC进行设置。

使用-XX:ConcGCThreads设置并发线程数量。

CMS并不是独占的回收器,也就是说CMS回收过程中,应用程序仍然在不停的工作,又会有新的垃圾不断的产生,所以在使用CMS的过程中,应该确保应用程序的内存足够可用。CMS不会等到应用程序饱和的时候才去回收垃圾,而是在某一阀值的时候开始回收,回收阀值可用指定的参数进行配置,-XX:CMSlnitiatingOccupancyFraction来指定,默认68,也就是说当老年代的空间使用率达到68%的时候,会执行CMS回收。如果内存使用率增长的很快,在CMS执行的过程中,已经出现了内存不足的情况,此时,CNS回收就会失败,虚拟机将启动老年代串行回收器进行垃圾回收,这回导致应用程序中断,直到垃圾回收完成后才会正常工作,这个过程GC的停顿时间可能较长,所以-XX:CMSlnitiatingOccupancyFraction的设置需要根据实际的情况。

标记清楚法有个缺点就是存在内存碎片的问题,那么CMS有个参数设置-XX:+UseCMSCompactAtFullCollecion可以使CMS回收完成之后,进行一次碎片整理,-XX:CMSFullGCsBeforeCompaction参数可以设置进行多少次CMS回收之后,对内存进行一次压缩。

 

以上是关于JVM垃圾回收的主要内容,如果未能解决你的问题,请参考以下文章

JVM垃圾回收器

JVM垃圾回收机制 (垃圾判断,垃圾回收算法,垃圾回收器,五种引用)jvm

JVM的垃圾回收机制 总结(垃圾收集回收算法垃圾回收器)

JVM的垃圾回收机制 总结(垃圾收集回收算法垃圾回收器)

JVM垃圾回收优化实战-G1垃圾回收器

JVM垃圾回收篇(垃圾回收算法)