jvm学习笔记4--堆区和方法区的对象回收判别

6 篇文章 0 订阅

复习

前面有提到jvm的内存模型,我们知道,程序计数器、虚拟机栈和本地方法栈是和线程同生共死的,每个栈帧的大小在类结构确定下来之后就都是已知的。因此这几个区域的内存分配和回收都是有确定性的。

然而,java堆和方法区却有着显著的不确定性。只有程序在运行期间,我们才知道具体要创建哪些对象,创建多少对象,分配多少内存。这部分的内存分配和回收都是动态的。所以垃圾回收器所关心的就是这部分内存的管理。

复习完之前的内容,我们就知道了垃圾回收器的关心重点在于堆内存的对象管理方法区管理

在垃圾回收器回收内存之前,要做的第一件事,就是判别哪些对象实例需要被回收?换句话说,就是要判断哪些对象”存活“,哪些对象已经“死去”。

死去就是说对象不可能被任何途径所使用


对象的生死判别

引用计数法

引用计数法是一种常见的判别对象是否存活的方式,使用的方式是给每一个对象添加一个引用计数器。

当有一个地方引用它时,计数器加1;当引用失效时,计数器减1。
任何时候引用计数器为0就表示这个对象不能再被使用。

优点:原理简单,判定效率高
缺点:需要大量额外的处理才能保证正确的工作,如对象之间循环相互引用
语言:Python、微软的COM等在使用该方式。jvm没有使用该方式

可达性分析算法

当前主流的语言大多采用该方法来判断对象的“生死”。

基本思路:通过一系列被称为“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程中走过的路被称为“引用链”,如果某个对象到“GC Roots”没有任何的引用链相连,或者说从根节点到该对象不可达时,说明该对象是不可能再被使用的。如图所示:

图片替换文本

图中object5、object6、object7虽然相互有关联,但是他们到“GC Roots”是不可达的,所以会被判定为可回收的对象。

再java技术体系中,固定可作为“GC Roots”的对象包括以下几种:

  • java虚拟机栈中本地变量表中引用的对象,比如各个线程中被调用的方法堆栈中使用到的参数、局部变量和临时变量等;
  • 在方法区中类静态属性引用的对象;
  • 方法区中常量引用的对象;
  • 本地方法栈中JNI(native方法)引用的对象;
  • 虚拟机内部的引用,如基础数据类型对应的class对象,一些常驻的异常对象,还有系统类加载器等;
  • 所有被同步所只有的对象;
  • 反应java虚拟机内部情况的JMXBean、JVMNI中注册的回调、本地代码缓存等;

除了以上的这些,不同的垃圾回收器根据回收的区域不同,会临时加一些对象进去,共同构成完整的“GC Roots”集合


引用

无论是通过引用计数法,还是判断对象的引用链可达,都要根据“引用”来判断,所以都和“引用”脱离不了关系。

比如有这样一个场景,我们希望在内存足够的时候,对象可以保留在内存中;当在进行垃圾回收之后内存依然紧张时,就可以抛弃这些对象——很多的系统缓存功能否符合这样的场景。

根据场景,我们将引用分为:强引用、软引用、弱引用、虚引用四类,这4中类型的引用强度依次减弱。

强引用

即类似于

Object obj = new Object();

只要强引用关系还存在,垃圾回收器永远不会回收掉被引用的对象。

软引用

用来描述一些还有用,但非必须的对象。只要被软引用关联的对象,在系统将要发生内存溢出异常之前,会把这些对象列为回收范围进行第二次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异常。

弱引用

也是用来描述非必须的对象,强度比软引用更弱一些,被弱引用关联的对象,只能存活到下一次垃圾回收发生为止。当垃圾回收器开始工作的时候,无论当前内存是否足够,都会回收掉只被弱引用关联的对象

虚引用

是最弱的一种引用关系,一个对象是否有虚引用存在,完全不会影响其生存时间,也无法通过虚引用获取一个对象实例。为一个对象设立虚引用关联的唯一目的就是在这个对象被回收时会收到一个系统通知


回收方法区

虽然在java堆中的一次回收可以回收70%-99%的内存空间,方法区的回收成果远低于此,但是在一些虚拟机中也会有方法区的回收设计。

方法区的垃圾收集主要回收2部分内容:废弃的常量和不再使用的类型。回收废弃常量的方式和java堆中的回收方式类似。

比如常量池中的一个字符串“java”,当前系统中没有一个字符串对象的值是“java”,换句话说,已经没有任何字符串对象引用常量池中的“java”常量,且虚拟机中也没有其他地方引用这个常量。这时候如果发生垃圾回收,该常量就会被清理出常量池。常量池中的其他类型、接口、方法、字段的符号引用都是类似。

判断一个常量是否废弃比较简单,但是判断一个类型是否属于“不再被使用的类”条件就比较苛刻了。需要同时满足3个条件:

  • 该类的所有实例都已经被回收,也就是说jav堆中不存在该类及任何派生类的实例
  • 加载该类的类加载器已经被回收
  • 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

java虚拟机允许对满足以上3个条件的无用类进行回收,但是并不是和对象一样,没有引用了就必然回收

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值