JAVA垃圾收集器(一)

在JAVA中,JVM虚拟机已经帮我们实现了Java垃圾内存的回收,在说JAVA垃圾收集器之前,先思考下面几个问题:

1)哪些内存需要回收?

2)什么时候回收?

3)如何回收?

闭着眼睛思考1分钟…

好,下面依次来说下:

1,哪些内存需要回收?

我们知道JAVA内存模型包括:Java堆,方法区,线程栈(分为:程序计数器,虚拟机栈,本地方法栈)

线程栈(程序计数器,虚拟机栈,本地方法栈)这三个是随着线程而生,线程而灭。栈中的栈针随着方法的进入和退出有条不紊的执行者出栈和入栈的操作,每一个栈帧中分配多少内存基本上是在类结构确定下来就已知的,所以方法结束或线程结束后,内存自然就跟着回收了,所以这部分不用过多考虑内存回收的问题

Java堆和方法区,这些里面都是用来存一些静态的类,或者动态代理生成的类,创建的对象,这些都是在程序运行时动态创建的,所需要的内存我们就不确定了,如果创建的对象使用完后,一直没有被回收,就导致内存溢出了,所以这部分就是垃圾回收器所要关心的,他要识别出那些无效的对象回收掉

2,什么时候回收?

其实第一点已经说了,就是那些无效的对象,何为无效的对象?即不再可能被任何途径使用的对象

如何判断这个对象为无效对象呢?垃圾回收提供了2种算法:引用计数法和根搜索算法,

引用计数法:即判断某个对象不在被其它对象引用,当某个对象被一个对象引用时,计数器+1,当引用失效时,则-1,直到减为0时,表示可以回收了,但这不能解决A,B之间相互引用的问题,A中有一个变量指向B,同时B中有一个变量指向A,所以即使A,B最后都不会再用了,由于他们的引用关系还在,仍然不能被回收。

JAVA中使用的就是根搜索算法来判断对象是否存活的

根搜索算法:JAVA中定义了一个名为“GC Roots” 的对象来作为起始点,从这个节点开始找,如果某个对象到这个“GC Roots”对象没有引用连接,那么表示这个对象就可以被回收了。

哪些对象可以作为GC Roots呢?Java中定义了这些对象:

1)虚拟机栈(栈帧中的本地变量表)中引用的对象。

2)方法区中类静态属性引用的对象。

3)方法区中常量引用的对象

4)本地方法栈中(一般说的本地方法)应用的对象

如下图,object1,object2,object3,object4不能被回收,object5,object6,object7虽然有互连,但是可以被回收

75B97DA3-0F8F-43CB-9BDD-2B310EF88B90

3,如何回收?

关于回收有3中算法:

1)标记-清除算法

javaneicunbiaojiqingchu

如上图,先标记出无效的对象,再进行清理,它有两个缺点:

效率问题:标记和清除过程的效率都不高

空间问题:如上图,清除后会产生大量不连续的内存碎片

2)复制算法

javaneicunfuzhi

如上图,先将可用内存划为为大小相等的两块,每次只使用其中一块,当这一块内存用完了,将存活的对象复制到另一份上,并将原先那块一次性清理掉

缺点:以空间换时间,这样,可用内存就缩小为原来的一半了,当然JVM做了也做了优化

在使用这种算法回收新生代时,不用要求内存分为1:1等量的两块,因为新生代一般都是朝生夕死,所以只会有少部分对象存活下来,JVM将新生代分为了Eden和两块较小的Survivor,Eden和Survivor内存为8:1,当Survivor内存不够时,就需要其他内存(老年代)来担保为其承担部分内存。

3)标记-整理算法

javaneicunbiaojizhengli

如上图,类似于“标记-清理”,也是先标记处存活的对象,但之后是先将存活的对象都向一端移动,然后清理掉边界之外的内存,这种算法一般适用于JAVA老年代的回收

说明:JAVA堆分为了新生代和老年代

新生代每次回收都会有大批对象死去,只有少量存活,所以采用复制算法

而老年代每个对象的存活率很高,且没有额外的空间对其担保,所以可以采用“标记-清理”或“标记-整理”算法来回收

 

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注