Edwin & Xinyu's Blog  

查无此人,查有此地

Java基础之GC那些事(二) 有更新!

现在情人过节,每月一次。虐狗的日子还少吗?

 这不,今天农历七月的豪华恩爱餐已成吨上线!

 如此,对广大程(dan)序(shen)员(gou)而言,如何快速高效省地地吃完狗粮就值得好好探讨。要知道对于普通程序员而言,狗(JVM)粮(GC)并不是容易吃(理)到(解)。

 首先,我们来假定一下吃狗粮的场景:某空地有12个盘子可存放狗粮,工作人员(JVM工作线程)A和B不定时在空盘放入食物X,Y或Z(Java对象)。场地上有永远吃不饱的狗(GC线程)。倾向性:工作人员倾向尽可能以最快速度尽可能多地投放狗粮;狗倾向于快速吃完食物;喂食场地倾向于最大范围空盘。 规定: 每个有食物的盘子同一时间只能被一只狗吃;每个狗一次只能吃光一个盘子的食物;每个狗于吃某工作人员后序号的食物的前提是它把该工作人员前序号的食物吃完,也就是说如果工作人员A投放了XZ两种食物,狗在X吃完前是不能吃Z的;工作人员时不时会失误在一个盘子里混入两个工作人员的多种食物;若某工作人员在场,狗不得吃他投放的食物。

 那么,广大单身狗(GC)是如何来花式吃狗粮的呢?

 首先我们需要回答狗能吃哪个工作人员投放的食物。

 方案一,引用计数法:即每个工作人员上场,计数+1;工作人员离场,计数-1。狗只能吃计数为0的工作人员投放的食物。

 事实上,这个算法似乎很完美,但是一旦遇到工作人员失误出现一个盘子里同时放入两个工作人员的食物,就极易产生循环引用。即吃A工作人员食物要等B离场,但B离场后恰好A有上场了。

 方案二,根搜索法:即扫描场上每个最后序食物的投放者,如果该工作人员不在场(不可达)则食物可以使用(回收)。这是Java常规使用的方法。

 通常Java中GC roots对象包含:虚拟机栈中的引用的对象;方法区中的类静态属性引用的对象;方法区中的常量引用的对象;本地方法栈中JNI的引用的对象。当任意一个对象对GC roots不可达,则该对象可以被标识为可回收。

 接着,我们来回答狗如何最优吃某工作人员投放的食物的问题。(要知道,狗在吃工作人员食物就意味着其不能上场)

 方案一,复制算法:即将12个盘子区域一分为2。一次只使用6个盘子区域。当区域一6个盘子被工作人员近乎投满时,工作暂停(JVM工作线程暂停开始GC)。根据根搜索法判断在场的工作人员,将其所投放的食物移动至区域二(存活区)。随后放狗消灭区域一的所有食物。通常当JVM中存活对象很少时,该算法是高效的。

 方案二,标记清除算法:即根据根搜索法判断在场的工作人员并对其投放的食物标记,标记完成后,对未被标记的食物进行放狗回收。由于不需要移动对象,故在存活对象较多的场景下该算法比较高效,但会产生内存碎片。

 方案三,标记压缩算法:与标记清除算法类似,但在回收阶段将所有存活的对象移动至内存一侧,并消除此边界外的内存空间。通常当存活对象不多时,该算法高效且不产生内存碎片。

 事实上,上述算法只是GC的基础算法。参考上次[Java基础之GC那些事(一)](http://mp.weixin.qq.com/s?__biz=MzI4ODE5OTY3MA==&mid=2654902037&idx=1&sn=de57b72b7c1fad31ac05b63c2178c736&chksm=f008318cc77fb89a3345ebee9d0f3eada487d5355ec322e34633b40e44869229752c2cd29e70&scene=21#wechat_redirect)中的Java对象分配流程,根据其不同特点,出现不同的GC算法实现:
  • Serial GC:单线程。年轻代:复制算法;老年代:标记压缩算法。此算法一般只适合于单CPU,JVM暂停时间要求不高的场景。

  • ParNew:年轻代:多线程复制算法。老年代:标记压缩算法。

  • Parallel Scavenge:基于内存复制并发。此算法基于程序吞吐量最优

  • CMS(Concurrent Mark-Sweep):分为四阶段:暂停程序进行标记,并发恢复程序进行标记,暂停程序进行重新标记和并发恢复程序进行清理。此算法基于程序响应最优,适合多Core场景。但由于标记清除算法的原因,其会产生大量内存碎片。一般在Full GC后进行内存压缩。

  • G1(Garbage First):JDK7出现的新算法,基于标记压缩算法。将堆划分为若干个区域(Region),它仍然属于分代收集器。不过,这些区域的一部分包含新生代,新生代的垃圾收集依然采用暂停所有应用线程的方式,将存活对象拷贝到老年代或者Survivor空间。老年代也分成很多区域,G1收集器通过将对象从一个区域复制到另外一个区域,完成了清理工作。此算法可精确控制应用GC暂停。

 GC算法大致介绍完了。**站在**[**架构**](http://mp.weixin.qq.com/s?__biz=MzI4ODE5OTY3MA==&mid=2654902055&idx=1&sn=1fe0223f87630f86a0b75630a8085083&chksm=f00831bec77fb8a86a0124edfb29a06cb4fbf2d78a43608835d6bb60fca17245b84da42f97f5&scene=21#wechat_redirect)**的立场上再看GC,眼中看到的不再是算法,而是道,是平衡****。**如何在程序响应时间,程序吞吐量和程序内存可用性上获得**动态的平衡**,这才是GC的精髓所在。**根据熵增理论,任何系统会自发地由有序向逐步向无序发展。**既然无法一蹴而就,如何以20%精力解决80%内存回收问题?于是乎便有了JVM堆区域的年轻代,老年代。便有了那些各有千秋的GC算法。

validate