码迷,mamicode.com
首页 > 其他好文 > 详细

谈一谈垃圾回收器

时间:2016-12-27 01:06:23      阅读:213      评论:0      收藏:0      [点我收藏+]

标签:new   分配   问题:   arc   instance   vc6   bsp   images   icc   

目的:

 

使用垃圾回收器的唯一原因就是:回收程序不再使用的内存。

 

针对的目标对象:

 

Java的垃圾回收器会自动回收不再使用的Java对象,释放内存。但是回收的是用new创建的,分配在堆上的内存。

 

finalize():

 

那么,如果不是用这种方式创建的对象,该怎么回收?比如:Java调用了本地的c语言方法创建了个对象,那么这时,该对象不是放在堆上的。除非你手动去调用c的free()方法,否则,这个对象将永远不会被清理。

 

Java的finalize()方法可以解决上面的问题。垃圾回收器在回收垃圾对象时,会首先去调用该对象的finalize()方法。所以,你可以在finalize()方法中调用c的free()方法。

 

一般教科书会写,finalize()用于垃圾回收之前的清理工作,而实际上,除了上面讲的极少数情况之外,我们一般情况下并不需要使用finalize()。

 

不保证发生:

 

虽然Java的垃圾回收器会根据对象的使用情况自动清理内存,但并不一定会发生,如果内存还够用的话,虚拟机一般是不会浪费时间去作清理工作的。

 

如何判断Java对象可以回收:

 

1.不被使用的“引用计数器法”:

 

每个对象都含有一个引用计数器,当有引用变量指向该对象时,引用计数器+1,当这个引用变量不再指向该对象,或者被置为null时,计数器-1。如下图:

 

技术分享

 

当第四种情况发生时,即:没有引用变量指向“李四”那个对象了,这时,垃圾回收器在恰当的时候就会把李四所在的对象回收掉。

 

它简单便捷,但是之所以没被Java虚拟机采用的原因是:无法解决循环引用的问题。举个简单的例子:

 

技术分享

 

objA有个instance变量,objB也有个instance变量,让objA的instance指向B对象,而让objB的instance变量指向A对象,那么,B对象和A对象的引用计数器都是1,不为0,如果按照引用计数器的方法,A和B就不能被回收,但事实是,objA和objB这两个引用变量已经是null了(它们指向的具体对象已经不再被引用了)。

 

2.根搜索算法

 

在主流的商用程序语言中(Java和C#,甚至古老的人Lisp语言),都是使用根搜索算法(GC Roots Tracing)判定对象是否存活的。

 

之前讲过,对象的引用是放在栈中的,常量的引用是放在常量池之中的。如图:

 

技术分享

 

根搜索算法的思想是,从常量池和栈中的引用变量开始遍历所有的引用变量,找到所有的活的对象(引用不为null)。然后再继续寻找这个对象所包含的所有引用,反复进行,直到所有引用网络被访问完为止。

 

常量池或栈中的引用变量是根节点,扩展出的整个网络就是一个引用链。最后,如果最终发现有对象到根节点的路径是不可达的,说明这个对象是可回收的,这就解决了循环引用的问题:

 

技术分享

如上图,GCRoots是根节点,object5、6、7虽然各自引用,但是它们到GCRoots都是不可达的,所以,它们是可以被回收的。

 

怎样回收?

 

每个虚拟机采用的回收算法是不同的,经典的案例如下:

 

标记-清除算法:

 

在使用“根搜索算法”寻找引用变量的同时,虚拟机会给每个存活的对象做一个标记,全部标记完成的时候才进行清除工作。

 

这样的问题是,存活的对象在堆中不是连续存储的,那么清除“死亡”对象后,内存中就会留下大量碎片,如果在后面需要用到大内存对象时,内存空间不够,就要重新整理内存。如图回收前:

 

技术分享
 

回收后:

 

技术分享

技术分享

 

复制算法:

 

它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。如图回收前:

 

技术分享

 

回收后(把存活着的对象搬到右侧,左侧剩下的就都是可清理的,然后统统清理掉。当右侧需要清理的时候,类似的,把存活的对象再搬到左侧,然后清空右侧):

 

技术分享

 

这种方式的缺点:很显然,可用内存只有原来的一半儿。还有个缺点:如果左侧大量的都是存活的对象,清理时仍然要全部搬到右侧,很浪费时间。

 

现在的商业虚拟机都采用这种收集算法,但是保留区与运作区的比例有不同,且详细又将堆内存划分为新生代、老年代。新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。关于新生代、老年代、堆内存等,详细可查阅关于Java虚拟机的资料了解。

 

 

 


参考资料:

1.Thinking in Java 第5.5.4章节。

2.cnblogs:Java垃圾收集器:

http://www.cnblogs.com/gw811/archive/2012/10/19/2730258.html

3.blogjava:Java堆内存:

http://www.blogjava.net/fancydeepin/archive/2013/09/29/jvm_heep.html

 

更多内容请关注微信订阅号:it_pupil

技术分享

谈一谈垃圾回收器

标签:new   分配   问题:   arc   instance   vc6   bsp   images   icc   

原文地址:http://www.cnblogs.com/mesopotamia/p/6224250.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!