zoukankan      html  css  js  c++  java
  • 谈一谈垃圾回收器

    目的:

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

    针对的目标对象:

    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

  • 相关阅读:
    烂泥:学习ubuntu之快速搭建LNMP环境
    烂泥:学习ubuntu远程桌面(二):远程桌面会话管理
    烂泥:学习ubuntu远程桌面(一):配置远程桌面
    烂泥:学习ssh之ssh密钥随身携带
    烂泥:学习ssh之ssh无密码登陆
    JS 获取浏览器窗口大小
    connect() failed (111: Connection refused) while connecting to upstream的解决
    css加载没效果,查看网络显示类型为 text/plain 的解决方法
    empty和isset的区别
    SQLite3命令操作大全
  • 原文地址:https://www.cnblogs.com/mesopotamia/p/6224250.html
Copyright © 2011-2022 走看看