GC Root总结

本文深入探讨了GCRoots的概念及其实现原理,解释了如何通过一系列GCRoots对象作为起点来判断一个对象是否可达,从而决定其是否可以被垃圾回收器回收。文中详细列举了各种类型的GCRoots对象。

JVM根据GC Roots算法判定一个对象需要被回收,GC Roots一般在JVM的栈区域里产生。

GC Roots原理

GC Roots基本思路就是通过一系列的称为“GC Roots”的对象作为起始点, 从这些节点开始向下搜索, 搜索所走过的路径称为引用链( Reference Chain),当一个对象到 GC Roots 没有任何引用链相连( 用图论的话来 说,就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。

GC Roots对象

常说的GC(Garbage Collector) Roots,特指的是垃圾收集器(Garbage Collector)的对象,GC会收集那些不是GC Roots且没有被GC Roots引用的对象。

一个对象可以属于多个root,GC Roots有以下几种:

  • Class - 由系统类加载器(system class loader)加载的对象,这些类是不能够被回收的,他们可以以静态字段的方式保存持有其它对象。我们需要注意的一点就是,通过用户自定义的类加载器加载的类,除非相应的Java.lang.Class实例以其它的某种(或多种)方式成为roots,否则它们并不是roots,.
  • Thread - 活着的线程
  • Stack Local - Java方法的local变量或参数
  • JNI Local - JNI方法的local变量或参数
  • JNI Global - 全局JNI引用
  • Monitor Used - 用于同步的监控对象
  • Held by JVM - 用于JVM特殊目的由GC保留的对象,但实际上这个与JVM的实现是有关的。可能已知的一些类型是:系统类加载器、一些JVM知道的重要的异常类、一些用于处理异常的预分配对象以及一些自定义的类加载器等。然而,JVM并没有为这些对象提供其它的信息,因此需要去确定哪些是属于"JVM持有"的了。

参考资料:GC roots

在 Java 和 Android 的垃圾回收机制中,**GC Roots** 是垃圾回收器用来判断一个对象是否可被回收的起点。**GC Root** 是一组必须活跃的引用,从这些根节点出发,沿着引用链向下遍历,所有能被访问到的对象都被称为“**可达对象**”,不会被回收;而不能被访问到的对象则被认为是“**不可达对象**”,将被回收。 --- ### ✅ 常见的 GC Roots 类型 | 类型 | 描述 | |------|------| | **虚拟机栈中的局部变量表中的引用对象** | 例如:方法中定义的 `Object o = new Object();`,`o` 是一个局部变量,是 GC Root | | **方法区中类静态属性引用的对象** | 例如:`public static User user;`,`user` 是 GC Root | | **方法区中常量引用的对象** | 例如:`public static final String NAME = "Tom";`,`NAME` 是 GC Root | | **本地方法(JNI)引用的对象** | 例如:通过 Native 方法创建并返回的 Java 对象引用 | | **活跃的线程对象(Thread)** | 正在运行的线程本身是一个 GC Root | | **类加载器(ClassLoader)** | 某些类加载器对象在类卸载前是 GC Root | | **Java 虚拟机内部对象** | 如异常对象、系统类加载器等 | | **同步锁(synchronized)持有的对象** | 被 synchronized 持有的对象引用也作为 GC Root | --- ### ✅ 示例说明 #### ✅ 1. 局部变量(虚拟机栈中的引用) ```java public void method() { Object obj = new Object(); // obj 是局部变量,属于 GC Root } ``` - 方法执行完后,`obj` 不再是 GC Root,指向的对象可被回收。 --- #### ✅ 2. 静态属性引用 ```java public class MyClass { public static Object obj = new Object(); // obj 是 GC Root } ``` - 只要 `MyClass` 没有被卸载,`obj` 一直可达,不会被回收。 --- #### ✅ 3. 常量引用(final static) ```java public class Constants { public static final String TAG = "GCRoot"; // TAG 是 GC Root } ``` - 常量在编译期就被确定,属于 GC Root。 --- #### ✅ 4. JNI(本地方法)引用 ```java // Native 方法返回一个 Java 对象 native Object createNativeObject(); ``` - 由 Native 层创建并返回的对象,被 JNI 引用,属于 GC Root。 --- #### ✅ 5. 活跃线程 ```java new Thread(() -> { Object obj = new Object(); // obj 在线程执行期间是可达的 }).start(); ``` - 只要线程还在运行,线程中引用的对象不会被回收。 --- #### ✅ 6. 类加载器 - 每个类都有一个关联的类加载器,只要类加载器存在,其加载的类就不会被卸载。 --- ### ✅ 内存泄漏与 GC Roots 的关系 内存泄漏的本质是:**无用的对象被 GC Root 引用链持有,无法被回收**。 例如: ```java public class LeakManager { private static Object leakObject; public static void setLeak(Object obj) { leakObject = obj; // 静态变量导致 obj 无法被回收 } } ``` - 如果 `obj` 是一个 `Activity`,即使 `Activity` 已经 finish,由于 `leakObject` 是静态变量(GC Root),导致 `Activity` 无法回收,形成内存泄漏。 --- ### ✅ 如何查看 GC Roots? - 使用内存分析工具如 **Eclipse MAT(Memory Analyzer)** 或 **Android Studio Profiler**。 - 在内存快照中,选择一个疑似泄漏的对象,右键选择 **“Path to GC Roots”**,即可看到哪些引用链导致该对象无法被回收。 --- ### ✅ 总结 | GC Root 类型 | 是否常导致内存泄漏? | 示例 | |--------------|---------------------|------| | 局部变量 | 否(生命周期短) | 方法内的对象 | | 静态属性 | ✅ 是常见原因 | `public static Context context;` | | 常量引用 | 否 | `public static final String TAG` | | JNI 引用 | ✅ 可能 | Native 创建的对象 | | 线程 | ✅ 可能 | 未结束的线程 | | 类加载器 | 否 | 系统自动管理 | | 同步锁 | ✅ 少见 | `synchronized(obj)` 中的 obj | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值