? ? ? ? 在对象中添加一个引用计数器,当每有一个地方引用它时,计数器值加一。当引用失效时,计数器值就减一。当一个对象的计数器为零时,表示该对象没有被任何其他对象引用,因此可以被释放。
????????通过 一系列称为“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”,如果某个对象到GC Roots间没有任何引用链相连, 或者用图论的话来说就是从GC Roots到这个对象不可达时,则证明此对象是不可能再被使用的。
GC Roots:虚拟机栈中引用的对象、在方法区中类静态属性引用的对象、在方法区中常量引用的对象、在本地方法栈中JNI引用的对象、所有被同步锁持有的对象。
图中虽然object4、object5、object6之间有互相引用,但它们到GC Roots不可达,所以它们被判定为可回收对象。
再介绍一下引用的区别:
再将回收算法前我们需要了解两个假说:
这两个分代假说共同奠定了多款常用的垃圾收集器的一致的设计原则:收集器应该将Java堆划分 出不同的区域,然后将回收对象依据其年龄(年龄即对象熬过垃圾收集过程的次数)分配到不同的区域之中存储。
? ? ? ? 这个算法正如他的名字一样,算法分为“标记”和清除两个阶段。先把需要清除的对象进行标记,再标记完成后,统一回收掉所有被标记的对象,也可反过来,标记存活的对象,统一回收所有未被标记的对象。这是最基础的收集算法,后续大多数的收集算法都是以标记清除算法为基础。
缺点:第一个执行效率不稳定,如果有大量的对象需要清除,那么要进行大量的标记和清除,效率随回收对象的增长而降低(所以比较适合老年代)。第二个内存空间会存在碎片化问题,使其原本有足够大空间,但由于太过碎片化无法存大对象,从而不得不提前触发垃圾收集。
? ? ? ? 这个算法主要是用来解决当有大量需要回收的对象时效率低下的问题。标记的不要回收的对象,但我们要操作的是不需要回收的对象,此时我们所需要的时间就会少很多。这个方法则是半区复制法。我们将内存等半分,每次只使用一块,当第一快内存用完之后,只要将还存活的对象复制到第二块去,然后将第一块内存清空。这样往复操作。这个操作简单高效,还解决了内存碎片化问题。缺点就是每次内存只能使用一半,太过于浪费。
现在好多JVM大多使用这样是收集算法去回收新生代,但是并不是1:1的比例,而是将其划分为? Eden(8):Survivor(1):Survivor(1),每次分配内存只使用Eden和其中一块Survuvor,当发生垃圾收集时,将这个块Survuvor和Eden中存活的对象复制到空闲那块Survuvor上去,然后之间清空这个块Survuvor和Eden。这个比例是通过测试得来的合理分配。这样一来才浪费了10%的内存。可以接受。当然如果出现了某次意外,一个Survivor无法存放所有的存活对象,可以向老年代“借”内存用来存放。
标记整理算法就是将需要回收的对象标记然后清除,最后将存活下来的对象进行整理。这样子即利用了100%的内存空间,又解决内存碎片化问题。但是如果是向老年区这个每次清除都有大量的对象存活的区域,那么移动对象也是很大的负担。所以一般都是先进行标记清除算法,待碎片化太过于严重再使用一次标记整理算法,解决碎片化。这样的方式被CMS收集器使用着。
ps:移动过程中要停止用户线程。
?