Java 垃圾收集器与内存分配策略(一):对象“死亡判断”和四种引用

来源:互联网 发布:万网域名证书生成 编辑:程序博客网 时间:2024/06/11 21:34

这里讨论的JVM虚拟机中的垃圾回收区域是指在线程共有的部分:Java堆(Java Heap) 与 方法区。

在判断一个对象已经死亡的方法有两种:

  • 引用计数算法
  • 可达性分析算法

引用计数算法

在对象中添加一个引用计数器,每当有一个地方应用对象,计数器数值 +1;如果某个引用失效时,计数器数值 -1; 如果计数器数值为0就说明对象不可能再被使用。这种方法实现简单,判定的效率也很高,但是目前主流的Java虚拟机当中没有用这个方法来管理内存,其中最主要的原因是它无法解决对象相互循环引用的问题。

class MyObject {    ...    Object instance;    ...}class Test {    ...    public static void try() {        MyObject objA = new MyObject();        MyObject objB = new MyObject();        objA.instance = objB;        objB.instance = objA;        objA = null;        objB = null;    }    ...}

在这种情况下,当前两个对象互相保存了对方的应用,但是这个两个对象已经不可能再被访问,这导致它们的引用计数器数值不为0,所以无法被GC收集器回收。

可达性分析算法

这个算法的基本实现就是通过一系列的成为“GC Roots”的对象作为起点,从这些节点开始向下搜索,所有走过的路径(其实每个节点都是对象)就称为引用链。如果一个对象没有路径可以到达GC Roots,或者说GC Roots和它们处于一种不可达的状态时,它们会被判定为可回收的对象。
这种算法比较抽象。。。


再谈引用

在Java中原本对于引用的描述太过纯粹和狭隘。一个对象只有被引用或没有被引用两种状态,对于如何描述一些“食之无味,弃之可惜”的对象就很无力。
所以为了满足那些对象:在内存空间还足够时,则保留这些对象在内存中;如果内存空间在进行垃圾手机之后还是非常紧张,那就回收这些对象。
对此,定义了四种对象的应用,他们分别是:

  • 强引用: 类似“Object obj = new Object()”这类的应用,只要这种强引用还存在,垃圾收集器就永远不会手机这些对应的内存空间。

  • 软引用: 用来表示一些还有用但是非必须的一些对象。对于这些对象,在虚拟机进行过内存回收之后如果还是没有足够的内存来使用,就会对这些软引用的对象进行回收。关于软引用的实现方法就是在JDK 1.2之后的SoftReference类。

  • 弱引用: 用来描述非必须的对象,但是它的强度比软引用还是要弱一点。它“命短”,每次垃圾收集器进行垃圾回收时,弱引用都会被作为垃圾回收,不论内存是否足够。

  • 虚引用 : 对于一个对象来说,是否存在虚引用不会对其生命周期造成任何影响。为一个对象设置虚引用关联的卫衣目的就是能在这个对象被当做垃圾回收的时候收到一个系统通知。


另外说一下Java对象的finalize()方法,这里是一段网上关于它的解释:

finalize 的常规协定是:当 JavaVM 虚拟机已确定尚未终止的任何线程无法再通过任何方法访问此对象时,将调用此方法,除非由于准备终止的其他某个对象或类的终结操作执行了某个操作。finalize 方法可以采取任何操作,其中包括再次使此对象对其他线程可用;不过,finalize 的主要目的是在不可撤消地丢弃对象之前执行清除操作。例如,表示输入/输出连接的对象的 finalize 方法可执行显式 I/O 事务,以便在永久丢弃对象之前中断连接。
对于任何给定对象,Java 虚拟机最多只调用一次 finalize 方法。
finalize 方法抛出的任何异常都会导致此对象的终结操作停止,但可以通过其他方法忽略它。搜索
finalize就是一个对象的遗书,你把一个对象给杀了总的让他说点什么把。只不过你要注意finalize是垃圾回收启动来清理对象才来调用的,就算自己掉这个方法这个对象也不会就从内存消失的,代码楼上就行。还有就是这个遗书可以把自己复活,另外如果在这个方法抛异常会导致此对象的终结操作停止,这两种都是死而复生的手段。


在GC Roots进行可达性分析标记的时候会对不可达的对象进行一次标记,在标记之后对象如果没有重载或者调用finalize方法,那么这个对象就会被回收,如果调用或者重载了该方法那就执行它,在该方法中可以对本对象进行自救,比如说抛出一个异常,或者使自己重新加入引用计数链。

就是某个对象无法访问->此对象死亡->自动调用finalize方法->这个方法抛出异常或者将自己添加到引用链中(通过这两种方式可以避免死亡,自救)->被垃圾收集器回收
但是我从《深入理解Java虚拟机》中读到内容是尽量不要使用这个方法,让我们忘记这个方法。。。。


对象如何进入老年代

首先第一个就是大对象,典型的就是需要一个连续内存空间的数组或者是字符串
或者是长期存活的对象,对象年龄计数器数值超过15就会进入老年代
再或者是担保机制,在“标记-整理”方法中,会预留一个Survivor空间作为使用,如果这个Survivor空间不够用,那就存入老年代。

0 0
原创粉丝点击