JVM之垃圾回收

       Java不像C++那样,申请内存,还需要程序员控制去回收内存,控制不好就会内存泄露。因为JVM有垃圾回收这个东东,所以java程序员可以轻松一些,但是一但java内存泄露,要是不了解java内存和垃圾回收,那么解决问题从何谈起。所以就有那句“Java和C++内存就像围城,在里面的人想出来,而在外边的人想进去”。

        

       java内存结构之前博文中有讲解过,这篇文章侧重点在介绍垃圾回收,本文主要从以下几个方面介绍一下垃圾回收。首先介绍什么样的内存需要进行回收,其次介绍垃圾回收算法,最后介绍一下垃圾回收器。

       1、什么样的对象需要进行回收

       jvm判断一个对象是否可以回收,可以被回收的对象会进行标记,然后进行回收,标记会被标记两次。那么什么样的对象会进行标记呢?使用的是GC Root Tracing算法。首先就是根据GC Root进行搜索,判断对象是否可达,如果可达,则不进行标记,如果对象不可达,则进行标记。说了这么多,觉的还不如上图说的清楚一些,如图:

      

     

      那么什么样子的对象才是Root呢,主要有以下几种:

      1)虚拟机栈中本地变量表中reference引用对象

      2)方法区中静态变量引用对象

      3)方法区中的常量

      4)本地方法栈中引用的对象

      2、jvm中的垃圾回收算法

       jvm垃圾回收算法有,标记-清除算法,标记-复制算法、标记整理算法和分代回收算法。

       标记-清除垃圾回收算法实现简单,但是会产生内存碎片。

        

       标记-复制算法不会产生内存碎片,但是会浪费一半内存空间。

       

       标记-整理算法需要复制内存,如果在对象存活率比较高的时候,效率较低。

       

       分代回收算法可分为新生代(又被分为Eden区、Survivor区域),老生代。新生代对象大部分朝生夕死,一般采用标记-复制算法,老年代对象存活率较高,一半采用标记清楚或者标记整理算法。可以使用参数-Xmn指定新生代大小,-XX:SurvivorRatio指定Eden区域与Survivor占比,默认为8,代表Eden : Survivor = 8:1。

       

       3、垃圾回收器:

       第二部分说的是理论,那么这一部分讨论的就是实现,jvm虚拟机规范并没有规定如果实现垃圾回收器。本文就讨论一下hotpot虚拟机中的垃圾回收器。接下来,本文从四个方面会介绍每款垃圾回收器。1)针对区域 2)使用的垃圾回收算法 3)串行,并行 4)可以使用的参数

        1)Serial/Serial Old回收器。

        Client模式下的算法,会暂停所有用户线程,单线程专心做垃圾回收(串行)。Serial针对的是新生代,Serial Old针对的是老生代。可使用参数-XX:+UseSerialGC参数(Serial+Serial Old收集器组合)

        2)ParNew/Parallel Old收集器。

        Server模式下的算法,ParNew收集器是Serial收集器的多线程版本(并行)。ParNew针对新生代,Parallel Old针对老生代。ParNew使用标记-复制算法,Parallel Old使用的是标记整理算法。可使用参数-XX:+UseParNewGC开启新生代的ParNew垃圾回收器(ParNew + Serial Old),-XX:ParallelGCThreads来指定线程数量,默认数量与CPU个数相同。

        3)Parallel Scavenge收集器(吞吐量有限收集器)

        Parallel Scavenge是一个新生代的收集器,使用的标记-复制算法,也是并行多线程收集器。与ParNew和CMS不同的是,它的关注点是达到一个可控的吞吐量。在Server模式下,使用-XX:UserParallelGC打开(Parallel Scavenge + Serial Old)或 UseParallelOldGC(Parallel Scavenge + Serial ParallelOldGC)。可使用-XX:MaxGCPauseMillis控制停顿时间和吞吐量大小-XX:GCTimeRatio控制吞吐量大小,数值在1-100之间,默认99,也就是1%(1/(1+99))的垃圾回收时间。-XX:UseAdaptiveSizePolicy开关参数,这个参数打开,不需要指定新生代大小、Eden区和Survivor区占比。虚拟机会根据当前系统运行状态,动态调整这些参数来达到最合适的停顿时间或最大吞吐量。

        4)CMS回收器

        CMS(Concurrent Marked Sweep)回收器是一个为了获取最短回收停顿时间为目的的收集器,针对于老年代。很多B/S系统上使用该收集器来获得最短系统响应时间。使用标记-清除算法。整个过程分为四步:(1)初始标记,标记与GC Root直接相关联的对象,单线程。STW(Stop the world) (2)并发标记,进行GC Root Tracing过程。并发,与用于线程并行 (3)重新标记,标记并发标记过程中用户线程运行导致标记产生变动的那一部分对象标记记录,并行,多个线程标记 (4)并发清除,与用户线程并发进行 。使用参数-XX:UseConcMarkSweepGC打开CMS回收器(默认使用ParNew + CMS + Serial Old(CMS失败后))

       

         算法缺点:

         (1)对CPU数量敏感,如果有两个CPU,那么并发标记阶段,有一个CPU要进行垃圾回收,会让程序变慢(降低50%),吞吐量降低。

         (2)无法收集浮动垃圾,浮动垃圾就是并发清除阶段用户线程产生的垃圾,只有在下一次垃圾回收时候,才会进行清除,称为浮动垃圾。如果CMS回收之后,产生浮动垃圾不足以让jvm分配新的内存,那么就会触发一次Full GC(使用Serial Old对老年代进行回收)。一般,可以使用参数-XX:CMSInitiatingOccupancyFranciton值来提高触发百分比,默认为68%,老年代使用了68%的时候,就会进行一次CMS就会进行垃圾回收。

         (3)标记-清除算法,产生垃圾碎片。可以使用参数-XX:+useCMSCompactAtFullCollection打开,进行CMS回收之后,进行一次碎片整理,但是碎片整理是无法并行的,或导致更长的停顿时间。可以使用-XX:CMSFullGCsBeforeCompaction,执行多少次Full GC之后,来一次碎片整理。

           5)G1收集器

           G1收集器针对是新生代,老生代,使用了标记-整理算法。G1设计的主要目的是 1)不产生空间碎片

2)可以非常精确的控制停顿时间,而且是在不希望吞吐量的前提下完成低停顿的内存回收。

           

          本文参考于《深入理解java虚拟机》

          GC Root Tracing图片来自于http://blog.csdn.net/time_hunter/article/details/12405127

          垃圾回收算法图片参考于http://www.cnblogs.com/wabi87547568/p/5282892.html

             

猜你喜欢

转载自chenghao666.iteye.com/blog/2384319