Java垃圾回收机制详谈

垃圾回收是在内存中存在没有引用的对象或者超过作用域的对象时进行的。
垃圾回收的目的是识别并且丢弃应用不再使用的对象来释放和重用资源。

垃圾回收机制中的算法
Java语言规范没有明确的说明JVM使用哪一种垃圾回收算法,但是任何一种垃圾回收算法一般要做两件基本的事情:(1)、发现无用信息对象。(2)、回收无用对象占用的内存空间,是该控件可被程序再次使用。

1、引用计数法
引用计数法是垃圾收集器中的早期策略。在这种方法中,堆中每个对象实例都有一个引用计数。当一个对象被创建时,且将该对象实例分配给一个变量,该变量计数设置为1。当任何其他变量被赋值为这个对象的引用时,计数加1(a=b,则b引用的对象实例的计数器加1),但当一个对象实例的某个引用超过了生命周期或者被设置为一个新值时,对象实例的引用计数器减一。任何引用计数器为0的对象实例可以被当作垃圾收集。当一个对象实例被垃圾收集时,他引用的任何对象实例的引用计数器减1。

优点:
引用计数器可以很快地执行,交织在程序运行中。对程序需要不被长时间打断的实时环境比较有利。

缺点:
无法检测出循环引用。如父对象有一个子对象的引用,子对象反过来引用父对象,这样,他们的引用计数器永远不可能为0。

public class Main{
	public static void main(String[] args){
		MyObject object1=new MyObject();
		MyObject object2=new MyObject();
		
		object1.object=object2;
		object2.object=object1;

		object1=null;
		object2=null;
	}
}

object1和object2指向的对象已经不可能再被访问,但是由于他们互相引用对方,导致他们的引用计数器都不为0,那么垃圾收集器就永远不会回收它们。

2、tracing算法(标记-清除算法)
在这里插入图片描述
根搜索算法是从离散数学中的图论引入的,程序把所有的引用关系看作一张图,从节点GC ROOT开始,寻找对应的引用节点,找到这个节点以后,继续寻找这个点的引用节点,当所有的引用节点寻找完毕后,剩余的节点则被认为是没有引用到的节点,即无用节点。

在java中可以作为GC ROOT的对象有:

  • 虚拟机栈中引用的对象(本地变量表)
  • 方法区中静态属性引用的对象
  • 方法区中常量引用的变量
  • 本地方法栈中引用的对象(Native对象)

示意图:
在这里插入图片描述
标记-清除算法采用从根集合进行扫描,对存活对象标记,标记完毕后,再扫描整个空间中未被标记的对象进行回收。
标记-清除算法不需要进行对象的移动,并且仅对不存活的对象进行处理,在存活对象比较多的情况下极为高效,但由于标记-清除算法直接回收不存活的对象,因此会造成内存碎片化。

3、compacting算法(标记-整理算法)
在这里插入图片描述
标记-整理算法在清除回收不存活对象占用的空间后,会将所有的存活对象王座端空闲空间移动,并更新对应的指针。因此成本更多,但是却解决了内存碎片的问题。

4、copying算法

该算法的提出是为了克服句柄的开销和解决碎片化的垃圾回收。它开始时把堆分为一个对象面和多个空闲面,程序从对象面为对象分配空间,当对象满了,基于copying算法的垃圾收集就从根集中扫描活动对象,并将每个活动对象复制到下面的空闲面,这样空闲面变成了对象面,原来的对象面变成了空闲面,程序会在新的对象中分配内存。

5、generation算法
分代的垃圾回收策略,是基于这样一个事实:不同的对象的生命周期是不一样的,因此不同生命周期的对象可以采用不同的回收算法,以便提高回收效率。

年轻代:

  1. 所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。
  2. 新生代内存按照8:1:1的比例分为一个eden对象和两个survivor(survivor0,survivor1)区。大大部分对象在Eden区中生成。回收时先将eden区存活的对象复制到一个survivior0区,然后清空eden区,当这个survivor0区也存放满了时,则将eden区和survivor0区存活对象复制到另一个survivor1区,然后清空eden和整个survivor0区,此时survivor0区是空的,然后将survivor0区和survivor1区交换,即保持survivor1区为空,如此往复。
  3. 当survivor1区不足以存放eden和survivor0的存活对象时,就将存活对象直接存放到老年代。若是老年代也满了就会触发一个Full GC,也就是新生代、老年代都进行回收。
  4. 新生代发生的GC也叫Minor GC,Minor GC发生频率比较高。

年老代:

  1. 在年轻代中经历了N次垃圾回收后仍然存活对象,就会被放到年老代中,因此,可以认为老年代中存放的都是一些生命周期较长的对象。
  2. 内存比新生代也大很多,大概比例为1:2,当老年代内存满时触发Major GC,即Full GC发生频率比较低,老年代对象存活时间比较长,存活率标记高。

持久化:
用于存放静态文件,如Java类、方法等。持久化对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate等,在这种时候需要设置一个比较大的持久化空间来存放这些运行过程中新增的类。

GC(垃圾收集器)
新生代收集器使用的收集器: Serial(复制算法),PraNew(停止-复制算法),Parallel Scavenge(停止-复制算法)
老年代收集器使用的收集器:Serial Old(标记-清除算法),Parallel Old(停止-复制算法),CMS(标记-清理算法)

GC的执行机制

Scavenge GC
一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到survivor区,然后整理survivor的两个区。这种方式的GC是对年轻代的Eden进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁发生。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章