对象的 生 | 死

6 篇文章 0 订阅

垃圾收集器需要完成的三件事:

  • 那些内存需要垃圾回收?
  • 什么时候需要回收?
  • 如何回收?

​ 线程私有的程序计数器、虚拟机栈、本地方法栈随着线程而生,随线程而灭,栈中的栈帧随着方法的进入和退出而有条不紊的执行者入栈和出栈。每一个栈帧中分配多少内存基本是在类结构确定下来时就已知的。,因此这几个区域的内存分配回收都具备确定性,在这几个区域内就不需要过多考虑垃圾回收的问题,当方法结束或者线程结束的时候,内存自然就跟着回收了。

​ 而Java堆和方法去这两个区域则有着很显著的不确定性:一个接口的多个实现类需要的内存可能不一样,一个方法所执行的不同条件的分支所需要的内存也可能不一样,只有处于运行期间,我们才能知道程序究竟会创建那些对象,创建多少个对象,这部分内存的分配和回收是动态的。垃圾回收器所关注的正是这部分的内存改如何进行管理。

如何判断对象已死
  1. 引用计数法

    ​ 在对象中添加一个引用计数器,每当有一个地方引用他时,计数器的值就+1;当引用失效时,计数器的值就-1;任何时刻计数器为零的对象就是不可能在被使用的。

    ​ 客观的说,引用计数算法虽然占用了一些额外的内存空间进行记数,但是他的原理简单,判定效率也很高。但是在Java领域中都没有选择引用计数器算法来管理内存,主要原因是,这个看似简单的算法有很多例外情况需要考虑,必须要配合大量的额外处理才能保证正确的工作,譬如单纯的引用计数器就很难解决对象之间相互循环引用的问题。

  2. 可达性分析算法

    ​ 当前主流的商用语言内存管理,都是通过可达性分析算法来判定对象是否存活的。这个算法的基本思路就是通过一系列成为“GC Roots”的跟对象作为起始节点集,从这些结点开始,根据引用关系向下搜索,搜索过的路径称为“引用链”,如果某个对象到GC Root没有任何引用链相连,或者图论的话来说就是从GC Root到这个对象不可达,则证明此对象是不可能再被使用的。

    GC Root对象都有那些:

    • 在虚拟机栈中引用的对象,譬如各个线程被调用方法堆栈中使用到的参数,局部变量、临时变量等
    • 在方法区中类静态属性引用的对象,譬如Java类的引用类型静态变量
    • 在方法去中常量引用的对象,譬如字符串称量池里的引用
    • 在本地方法栈中引用的对象
    • Java虚拟机内部的引用,如基本数据类型对象的class对象,一些常驻的异常对象以及系统类加载器
    • 被所有同步锁持有的对象

    除了这些固定的GC Root集合外,根据用户所选用的垃圾回收器以及当前回收的内存区域不同,还可以有其他对象临时性加入,共同构成完整的GC Root集合。

  3. 引用

    ​ 无论是通过引用计数算法判断对象的引用数量还是通过可达性分析算法判断对象是否引用链可达,判定对象是否存活都和“引用”离不开关系。在jdk1.2版本之前,Java里面的引用是很传统的定义:如果reference类型的数据中存储的数值是另一块内存的起始地址,就称该reference类型的数据是代表某块内存、某个对象的引用。这种定义并没有什么不对,知识现在看来有些狭隘了,一个对象在这种定义下只有被引用或者未被引用两种状态,对于描述一些“食之无味,弃之可惜”的对象就显得无能为力。譬如我们希望描述一类对象:当内存空间足够时,能够保存在内存空间中,如果空间在进行垃圾收集后仍然非常紧张,那么就可以抛弃这些对象

    ​ 在jdk1.2之后Java对于引用的概念进行了扩充分为:强引用(Strongly Reference)、软引用(Soft Reference),弱引用(Weak Reference)、虚引用(Phantom Reference)

    1. 强引用:是最传统引用的定义,是指在程序代码之中普遍存在的引用赋值,类似 Object obj = new Object(); 这种引用关系。无论在任何情况下,只要请引用关系还存在,垃圾回收器就永远不会回收被引用的对象
    2. 软引用:用来描述一些还有用,但非必须的对象。只有被软引用关联着的对象,在系统将要发生内存溢出的异常前,会把这些对象那个列入回收范围之内进行第二次回收,如果这次回收还没有足够的内存,才会抛出oom异常。
    3. 弱引用:也是用来描述那些非必须对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存道下一次垃圾收集发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收之被弱引用关联着的对象
    4. 虚引用:又称幽灵引用或幻影引用,他是最弱的一种引用关系。一个对象是否有虚引用存在,完全不会对其生存时间造成影响,也无法通过虚引用来区的一个对象的实例。为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统的通知
    4.生存还是死亡

    ​ 即使在可达性分析算法中判定为不可达的对象,也不是“非死不可的”,这时候他们还暂时处于“缓行期”,要真正旋盖一个对象死亡,至少要讲过两次标记过程:如果一个对象在进行可达性分析后发现,没有与GC ROOT相连的引用链,那么它就会被第一次标记,随后进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。假如对象没有覆盖

    finalize()方法,或者finalize()已经被执行过,那么虚拟机就将这两种情况都视为没有必要执行

    ​ 如果这个对象被判定为有必要执行finalize()方法,那么该对象会被放置在一个F-Queue队列之中这里所说的执行是指虚拟机会触发这个方法开始运行,但并不承若一定会的等到它运行结束。这样做的原因,如果某个对象的finalize()方法执行缓慢,或者更极端的发生了死循环,将可能导致F-Queue队列中的其他对象永久处于等待,甚至导致整个内存回收子系统的崩溃。finalize()方法是对象逃脱死亡命运的最后一次机会,稍后收集器将对F-Queue中的对象进行第二次小规模的标记,如果对相关要在finalize()中拯救自己——只要重新与引用链上的任何一个对象建立联系即可,譬如把自己赋值给某个类变量或者对象的成员变量,那在第二次标记时它将被移出“及将回收”的结合;如果对象这回时候还没有逃脱,那几本他就真的要被回收了。

    5.回收方法区

    ​ 方法区的垃圾回收主要分为两部分:废弃的常量和不再使用的类型。回收废弃的常量与回收Java堆中的对象十分类似。当没有任何对象引用方法区中的常量,且虚拟机中也没有其他贷方引用这和常量的话,就会被回收。常量池中的,接口、方法、字段的符号引用与与此类似。

    ​ 判断一个类是否属于“不再被使用的类”条件就比较苛刻,需要同时满足以下三个条件:

    • 该类的所有实例都已经被回收,也就是Java堆中不存在该类及其任何派生子类的实例
    • 加载该类的类加载器已经被回收
    • 该类对象的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值