JVM系列 - 何时回收何处对象
Last updated: Oct 289, 28089
回收何处对象
JVM 运行时内存数据区域包括:
- 程序计数器
- JVM 栈
- 本地方法栈
- JVM 堆
- 方法区
程序计数器、JVM 栈和本地方法栈的生命周期与线程相同
它们涉及的内存分配和回收具备确定性
因此,主要关注的就是内存分配和回收具备动态性的 JVM 堆 和 方法区
何时回收对象
一些不再被使用的对象驻留在内存中,占用内存资源,因此需要回收这些对象
如何判定 不再被使用 的对象:
引用计数算法
概念
在对象中添加一个引用计数器,
当有一个地方引用它时,计数器值加一
当引用失效时,计数器值减一
任何时刻计数器值为 0 的对象就是不可能再被使用的
使用者
微软的 COM、Python 语言都使用 Reference Counting
优点
原理简单,判定效率很高
缺点
- 占用一些额外内存
- 需要考虑很多例外情况,如 相互循环引用 就很难解决
可达性分析算法
概念
首先定义一些称为 “GC Roots” 的根对象作为起始节点集
从起始节点集开始,沿着引用关系搜索形成引用链(Reference Chain)
如果某个对象在这条引用链上,那么就认为该对象可达
如果某对象不在这条引用链上,就认为该对象不可达,即不可能再被使用的
GC Roots
包括:
- 在虚拟机栈(栈帧中的局部变量表)中引用的对象
- 方法区中类静态属性引用的对象
- 方法区常量引用的对象
- 本地方法栈 JNI 引用的对象
- JVM 内部的引用,如基本数据类型对应的 Class 对象,一些常驻的异常对象,系统类加载器
- 所有被同步锁持有(synchronized修饰)的对象
- 反映 JVM 内部情况的 JMXBean、JVMTI 中注册的回调、本地代码缓存等
使用者
Java、C# 和 Lisp 等等商用程序语言
引用
Java 在 JDK 1.2 之后对引用进行了扩充:
强引用
指使用 new 关键字赋值的引用
只要强引用关系存在,垃圾回收器就永远不会回收被引用的对象
软引用
使用 SoftReference 实现软引用,描述一些还有用、但非必须的对象
当内存不足将发生异常时,垃圾回收器会回收这部分内存
弱引用
使用 WeakReference 实现弱引用,描述一些非必须的对象
被弱引用关联的对象只能存活到下一次垃圾回收之前
无论内存是否足够,都会回收这部分内存
虚引用
使用 PhantomReference 实现虚引用
无法通过虚引用获取对象实例
唯一目的:能在对象被回收时收到一个系统通知
方法区的回收
方法区垃圾收集的性价比通常比较低,主要回收两部分内容:
- 废弃的常量
- 不再使用的类型
判断不再使用的类型需要满足:
- 该类的所有实例都被回收,该类和其子类的实例都不存在
- 加载该类的类加载器被回收
- 该类对应的 Class 对象没有在任何地方被引用
如果满足,则 JVM 被允许 对无用类进行回收,但也可以通过参数配置:
-Xnoclassgc
:控制是否对无用类型进行回收
-verbose:class
和 -XX:+TraceClassLoading
可以追踪类加载信息
-XX:+TraceClassUnLoading
可以追踪类卸载信息
场景
在大量使用 反射、动态代理、CGLib 等字节码框架,动态生成 JSP 等场景中,
通常需要 JVM 具备类型卸载能力,保证不会对 方法区 产生过大压力