【Java虚拟机】垃圾收集前的准备

13 篇文章 0 订阅

一、概述

在JVM所管理的内存区域中,有三块区域是不需要垃圾回收的,他们是本地方法栈,Java虚拟机栈,程序计数器。因为这三块内存区域是线程独立的,随着线程的创建而创建,线程执行完毕自然就随着线程灭亡。而且这三块区域的内存大小是可在编译器就确定下来的,他们的内存分配和回收都具备确定性,因此就不需要考虑垃圾回收的问题。
而Java堆不一样,程序只有在运行的时候才能知道应该创建多少对象,分配多少内存,这部分内存的分配和回收都是动态的,垃圾回收器关注的主要部分就是这块内存。

二、如何判断对象已死亡

目前普遍使用的方法是给对象加上一个引用计数器,当一个引用指向这个对象时,就给计数器加一,当一个引用失效时,就给计数器减一,任何时刻计数器为0的对象就是不可能再被使用的对象。但是,大部分虚拟机都没有用到这种方式来判断,因为这种方式很难解决对象间相互循环引用的问题。
举个简单的例子,看下面代码:
在这里插入图片描述
上面代码的对象objA和objB都有字段instance,赋值令objA.instance=objB以及objB.instance=objA,除此之外,这两个对象再无其他引用,但是他们互相引用着对方,导致他们们的计数器都不为0,所以使用计数算法无法通知GC收集器收集他们。
从运行结果我们可以知道,虚拟机并没有因为两个对象互相引用就不收集他们,这也说明了虚拟机没有使用计数算法来判断对象是否死亡。

三、可达性分析算法

目前主流的商用程序语言的主流实现中,都是通过可达性分析算法来判定对象是否存活的。这个算法的思路是通过一系列的“GC Roots”的对象作为起点,从这个这点往下搜索,搜索所走的路径叫做引用链,当一个对象到“GCRoots”没有任何引用链项链,则证明此对象是不可用的。如下图所示,对象object5,object6、object7虽然互相关联,但是他们到GC Roots 是不可达的,因此他们将会被判定为可回收的对象。
在这里插入图片描述
在Java语言中,可作为GC Roots的对象包括以下几种:

  1. Java虚拟机栈中局部变量表中引用的对象;
  2. 方法去中静态变量引用的对象;
  3. 方法区中常量引用的对象;
  4. 本地方法栈中的JNDI(一般说的Native方法)引用的对象;

四、引用

无论是通过计数算法判断对象的引用数量,还是可达性分析算法判断对象的引用链是否可达,判定对象的存活都与引用有关。在JDK1.2之前,引用是很狭隘的概念:如果reference类型中的数据中存储的是另外一块内存的起始地址,那么这块内存就代表着一个引用。在这样的定义下,对象只有两种状态:引用和未被引用。这就使得描述一些“食之无味,弃之可惜”的对象就显得无能为力。我们希望描述一类这样的对象:内存空间还足够时,则能保留在内存之中,当内存空间在进行垃圾回收后还是很紧张的情况下,则可以抛弃这些对象。很多系统的缓存场景都符合这样的应用场景。
在JDK1.2之后,Java对引用的概念进行了扩充,将引用分为强引用(Strong Reference),软引用(Soft Referrence)、弱引用(Weak Reference)和虚引用(Phantom Reference),引用强度依次减弱。
强引用就是指在代码中普遍存在的一种引用,类似于“Object obj = new Object()”这类的引用,只要强引用还存在,垃圾收集器就永远不会回收掉被引用的对象。
软引用是用来描述一些还有用但是非必需的对象。对于软引用关联着的对象,在系统发生内存溢出之前,会把软引用的对象作为回收对象进行第二次回收,如果回收之后还是没有足够的内存空间,才会抛出OutOfMemoryError异常。提供了SoftReference类来实现弱引用。
弱引用也是用来描述一些有用非必需的对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象只能存活到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收只被弱引用关联的对象。在JDK1.2之后,提供了WeakReference类来实现弱引用。
虚引用也成为幽灵引用和幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间造成影响,也无法通过虚引用了来获取一个对象的实例。为一个对象设置虚引用的唯一目的是能在这个对象被垃圾回收器回收时给系统发一个通知。提供了PhantomReference类来实现弱引用。

五、生存还是死亡

即使在可达性分析中不可达的的对象,也并非“非死不可”的,这时候他们处于“缓刑”阶段,要真正宣告一个对象的死亡必须经过两次标记过程:如果对象在进行可达性分析h后发现没有与GC Roots相连接的引用链,那么它会被第一次标记并且进行一次筛选,筛选的条件是此对象有没有必要执行finalize()方法。当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过了,这两种情况都会被虚拟机视为“没有必要执行”。
如果这个对象被判定为有必要执行finalize()方法,则这个对象会被放进一个叫做“F-Queue”的队列之中,并且在稍后由虚拟机自动创建的,优先级低的Finalizer线程去执行它。这里所谓的执行,指的是虚拟机会触发这个方法,但并不承诺会等待它运行结束。这是因为如果一个对象在finalize()方法中执行缓慢,或者发生了死循环,将可能使F-Queue队列中的所有对象等待,从而导致整个垃圾回收系统的崩溃。finalize()方法是对象逃脱的最后一次机会,稍后GC会对F-Queue中的对象进行小规模的二次标记,,如果对象要在finalize()方法逃脱–只需要重新与引用链上的某个对象进行关联即可,那么它会在第二次标记时被移出“即将回收”的集合;如果这时候对象没有逃脱,那么它就真的会被回收了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值