Java的GC机制

jvm 中,程序计数器、虚拟机栈、本地方法栈都是随线程而生随线程而灭,栈帧随着方法的进入和退出作入栈和出栈操做,实现了自动的内存清理,所以,咱们的内存垃圾回收主要集中于 堆和方法区中,在程序运行期间,这部份内存的分配和使用都是动态的。html


GC算法:

对象存活判断

引用计数法:每一个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时能够回收。
缺点是没法释放循环引用的对象。以下图:算法

clipboard.png

根搜索算法:从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证实此对象是不可用的。
在Java语言中,GC Roots包括:
虚拟机栈中引用的对象。
方法区中类静态属性实体引用的对象。
方法区中常量(final)引用的对象。
本地方法栈中JNI引用的对象。数组

clipboard.png
能够看到,该算法能够释放循环引用的对象(D和E)。jvm


垃圾收集算法

标记/清除算法:当堆中的有效内存空间(available memory)被耗尽的时候,就会中止整个程序(也被成为stop the world),而后进行两项工做,第一项则是标记,第二项则是清除。性能

clipboard.png

(1)标记:标记的过程其实就是,遍历全部的GC Roots,而后将全部GC Roots可达的对象标记为存活的对象。
(2)清除:清除的过程将遍历堆中全部的对象,将没有标记的对象所有清除掉。spa

缺点:一、首先,它的缺点就是效率比较低(递归与全堆对象遍历),并且在进行GC的时候,须要中止应用程序,这会致使用户体验很是差劲
二、第二点主要的缺点,则是这种方式清理出来的空闲内存是不连续的(碎片化),JVM就不得不维持一个内存的空闲列表,这又是一种开销。并且在分配数组对象的时候,寻找连续的内存空间会不太好找。.net

复制(copying)算法:将内存划分为两个区间,全部动态分配的对象都只能分配在其中一个区间(称为活动区间),而另一个区间(称为空闲区间)则是空闲的,当有效内存空间耗尽时,JVM将暂停程序运行,开启复制算法GC线程。将活动区间内的存活对象,所有复制到空闲区间,且严格按照内存地址依次排列,与此同时,GC线程将更新存活对象的内存引用地址指向新的内存地址。此时,空闲区间已经与活动区间交换,而垃圾对象如今已经所有留在了原来的活动区间。事实上,在活动区间转换为空间区间的同时,垃圾对象已经被一次性所有回收。线程

clipboard.png

缺点:一、它浪费了一半的内存。
二、若是对象的存活率很高,咱们能够极端一点,假设是100%存活,那么咱们须要将全部对象都复制一遍,并将全部引用地址重置一遍。复制这一工做所花费的时间,在对象存活率达到必定程度时,将会变的不可忽视。htm

标记/整理算法:标记/整理算法与标记/清除算法很是类似,它也是分为两个阶段:标记和整理。对象

clipboard.png

(1)标记:它的第一个阶段与标记/清除算法是如出一辙的,均是遍历GC Roots,而后将存活的对象标记。

(2)整理:移动全部存活的对象,且按照内存地址次序依次排列,而后将末端内存地址之后的内存所有回收。所以,第二阶段才称为整理阶段。
优势:标记/整理算法不只能够弥补标记/清除算法当中,内存区域分散的缺点,也消除了复制算法当中,内存减半的高额代价。
缺点:效率也不高,不只要标记全部存活对象,还要整理全部存活对象的引用地址。从效率上来讲,标记/整理算法要低于复制算法。

总结:一、三个算法都基于根搜索算法去判断一个对象是否应该被回收,而支撑根搜索算法能够正常工做的理论依据,就是语法中变量做用域的相关内容。所以,要想防止内存泄露,最根本的办法就是掌握好变量做用域,
二、在GC线程开启时,或者说GC过程开始时,它们都要暂停应用程序(stop the world)。
三、性能比较
效率:复制算法>标记/整理算法>标记/清除算法(此处的效率只是简单的对比时间复杂度,实际状况不必定如此)。
内存整齐度:复制算法=标记/整理算法>标记/清除算法。
内存利用率:标记/整理算法=标记/清除算法>复制算法。


分代收集算法

GC分代的基本假设:绝大部分对象的生命周期都很是短暂,存活时间短。
GC将对象数据进行分类。主要是两类:年轻代(Young Generation),老年代(Old Generation)。分代收集主要针对这两类的对象进行回收。

年轻代(Young Generation):年轻代含两种结构,伊甸园空间(1个,占80%)和幸存空间(2个,各占10%)。大多数对象会很快的变得不可达,所以,不少对象会在变成年轻代以后就消失,而这个过程咱们称之为“ minor GC”。因为存活率低,选用复制算法,一旦发生GC,将10%的幸存区间与另外80%伊甸园空间中存活的对象转移到10%的幸存空间,接下来,将以前90%的内存所有释放。

clipboard.png

年轻代遵循如下规则:
一般刚刚被建立的对象会存放在伊甸园空间。
伊甸园空间执行GC后,将Eden和From活着的对象一次性复制到另外一个名为To的Survivor中去,而后清理Eden和From

执行GC屡次后,依然存活的对象会被转移至老年代。

老年代(old Generation):对象来自新生代,如上所说部分对象会不可达,而剩下的从年轻代中存活下来,被拷贝至老年代。老年代所占用的空间要比年轻代多。如上,对象在老年代也会消失,而这个过程被称之为“major GC”(或者是 “full GC”).老年代中由于对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清除”或“标记-整理”算法来进行回收。

Permanebt Generation :持久代,或者称为方法区(method area),一般持久代用来保存类常量以及字符串常量。而特别须要注意这个持久代区域不是用来保存从老年代存活下来的对象的。持久代也能够发生GC。同时这个区域的GC会被看待为 major GC.(使用“标记-清除”或“标记-整理”算法)
永久代主要回收两种:常量池中的常量,无用的类信息。
要知道常量的回收是相对简单的,主要是无用的类回收比较麻烦,要注意如下几点:
类的实例已经所有被回收了
ClassLoader已经被回收
类的对象没有被引用

一般状况下,如下两种状况发生的时候,对象会重新生代区域转到年老带区域。
一、在年轻代里的每个对象,都会有一个年龄,当这些对象的年龄到达必定程度时(年龄就是熬过的GC次数,每次GC若是对象存活下来,则年龄加1),则会被转到年老代,而这个转入年老代的年龄值,通常在JVM中是能够设置的。

二、在年轻代存活对象占用的内存超过10%时,则多余的对象会放入年老代。这种时候,年老代就是新生代的“备用仓库”。

参考文章:
https://www.cnblogs.com/sunfi...
http://www.cnblogs.com/ityouk...
http://blog.csdn.net/u0116690...

相关文章
相关标签/搜索