java内存回收机制相关

来源:互联网 发布:网络暴力相关事件 编辑:程序博客网 时间:2024/06/05 10:10

对于垃圾回收,我们主要思考哪些内存需要回收、什么时候回收和如何回收几个问题。

哪些内存需要回收

堆中几乎存放着java中所有对象实例,垃圾收集器在对堆进行回收前,需要判断哪些对象是存活的,哪些对象是已经“挂掉”,并且是需要回收的。判断对象的存活与否,主要有引用计数法和根搜索算法。

如何判断对象需要回收

引用计数法:

给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器就减1;任何时刻,计数器都为0的对象就是不可能再被使用的。

总的来说,引用计数算法实现简单,判定效率也很高,但是它很难解决对象之间的相互循环引用问题,这也是java中并没有使用该方法的主要原因。

根搜索算法:

基本思路:通过一系列的名为“GC Roots”的对象作为起始点,从这些起始点开始向下搜索,搜索走过的路径称为引用链,当一个对象到GC Roots没有任何引用链时,也就是从GC Roots到这个对象不可达时,则说明该对象是不可用的。

其中在java里,“GC Roots”的对象包括虚拟机栈中的引用对象,方法区中的类静态属性引用的对象,方法区中的常量引用的对象,本地方法栈中JNI(即一般说的Native方法)中引用的对象

需要注意的是,即使该对象在根搜索算法中是不可达的,也不代表它肯定会被回收掉。要真正回收掉一个对象,至少要经历两次标记过程。如果对象在进行根搜索算法发现没有与GC Roots相连的引用链,它将会被第一次标记并且进行一次筛选,筛选的条件是此对象有没有必要执行finalize()方法。当对象没有覆盖finalize()方法或者finalize()方法已经被虚拟机调用过,虚拟机都将这两种情况视为“没有必要执行”,其中每个对象的finalize()方法都只会被系统自动调用一次。

如果对象被判定为有必要执行finalize()方法,那么这个对象会被放置在一个名为F-Queue的队列之中,稍后GC会对F-Queue中的对象进行第二次小规模的标记,如果对象要在finalize()中成功拯救自己,只需要重新与引用链上的任何一个对象关联即可,例如把自己(this关键字)复制给某个变量或对象的成员变量,那么在第二次标记时它将被移出“即将回收”的队列。

前面一直讲了引用,而在java中,不同的引用的作用都是不一样的,我们有必要了解一下引用的类型。

引用的类型:

在java1.2以前,java中的引用定义很传统:如果reference类型的数据中存储的数值的代表的是另外一块内存的起始地址,也就是堆中的对象所占用内存的起始地址,就称这块内存代表着一个引用。

但是,我们希望可以更灵活地进行内存的回收,某一些对象,当内存空间还足够时,即使该对象已经不可用了,但还可以保留在内存中,只有当内存进行垃圾回收还是非常紧张,则抛弃这些对象。为此,也就有了后来的强引用,弱引用,软引用,虚引用。

强引用:

Java中默认使用的就是强引用,类似“Object obj = new Obje()”这类的引用,只要强引用还存在,垃圾回收器永远不会回收掉引用的对象。

软引用

软引用关联的对象,只有在系统将要发生内存溢出异常之前,才会将这些对象列入回收范围并进行第二次回收。

弱引用

弱引用的对象只能存活到下次垃圾回收之前,无论系统内存是否充足,弱引用关联的对象都会被回收

虚引用

一个对象是否有虚引用的关联,完全不会对其生存时间构成影响,也无法通过虚引用来获取一个对象实例。为一个对象设置虚引用主要目的是希望这个对象被垃圾收集器回收时能收到一个系统通知。

接下来就是如何回收的问题了。

如何回收内存

垃圾收集算法主要有三种,标记-清除算法,复制算法,标记-整理算法。

标记-清除算法:

如同它的名字一样,算法主要分为两部分:“标记”和“清除”。
首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。它的标记过程也就是其那面所说的根搜索算法。它主要有两个问题:标记和清除过程的效率都不高,另外就是标记清除后产生大量不连续的内存碎片。
这里写图片描述

复制算法:

为了解决效率问题,“复制”的收集算法出现了。它将内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活的对象复制到另一块上面,然后再把已使用过的内存空间一次清理掉。这样就不用考虑内存碎片的问题了,每次只要移动堆顶指针,按顺序分配内存即可。
这里写图片描述

回收新生代使用的也是这种算法,但不是按照1:1的比例来划分内存空间,而是将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次只使用Eden和其中一块Survivor。回收时,将Eden和Survivor中还存活的对象一次性地拷贝到另外一块Survivor上,最后清理Eden和刚才使用过的Survivor的空间。

标记整理算法:

“复制”收集算法对存活率较高时要执行多次的复制操作,效率会变得很低。而在老年代中,对象存活率是比较高的,为此,提出了标记-整理算法。该算法与标记-清除算法较为相似,只是在后续步骤不仅仅只是对可回收的对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
这里写图片描述

原创粉丝点击