什么东西可以当做GC Root?
2 个回答
今朝有酒今朝醉,明日愁来明日愁。鬼魅举个更具体的例子来说明什么对象被称为 **GC Roots**,以及它们在垃圾回收中的角色: ### 例子:理解 GC Roots 在垃圾回收中的作用 假设我们有一个简单的 Java 程序,它创建了一些对象并执行了方法调用。代码结构如下: ```java public class GCRootExample { // 静态变量 private static Object staticVar = new Object(); public void exampleMethod() { // 局部变量 Object localVar = new Object(); // 调用另一个方法 anotherMethod(localVar); } public void anotherMethod(Object param) { // 本地方法中的引用 Object anotherLocalVar = new Object(); // 使用JNI方式创建本地对象 Object nativeRef = createNativeRef(); } } ``` 在这个例子中,程序创建了多个对象并在多个方法中持有引用。让我们一步一步看看垃圾回收器如何从 GC Roots 开始识别哪些对象是存活的,哪些是不可达的。 ### 为什么这些对象被称为 GC Roots #### 1. JVM 从 GC Roots 开始寻找存活对象 在程序执行过程中,GC Roots 是垃圾回收器的起点。我们来看这里有哪些 GC Roots: - **静态变量**:如 `GCRootExample.staticVar`。静态变量是整个程序运行期间都可以访问的,因此被视为 GC Roots。 - **局部变量(栈帧中的引用)**:如 `exampleMethod` 中的 `localVar` 和 `anotherMethod` 中的 `anotherLocalVar`。它们是线程栈中的局部变量,属于方法调用栈的一部分,也会被视为 GC Roots。 - **JNI 本地引用**:在 `anotherMethod` 中使用 JNI 创建的 `nativeRef` 对象引用。JVM 认为本地方法引用是“全局引用”,因此也是 GC Roots。 这些对象被称为 GC Roots 是因为 JVM 可以**直接访问**它们,而不依赖其他对象的引用。 #### 2. 从 GC Roots 开始的可达性分析 垃圾回收器从 GC Roots 开始扫描并递归查找所有与之相关联的对象。假设 GC 开始,整个过程如下: 1. **从 `staticVar` 开始**:垃圾回收器发现 `staticVar` 是 GC Roots 的一部分,因此标记 `staticVar` 和它所引用的对象为“存活”。 2. **方法调用栈中的局部变量**: - `exampleMethod` 的局部变量 `localVar` 被标记为“存活”,因为它直接可达。 - 进入 `anotherMethod`,`param` 参数和 `anotherLocalVar` 也都直接可达并标记为“存活”。 3. **JNI 本地引用**:`nativeRef` 作为 JNI 创建的引用也会被标记为“存活”。 #### 3. 无法从 GC Roots 访问的对象将被视为垃圾 在上述扫描过程中,只有可以通过 GC Roots 直接或间接访问到的对象才被认为是“存活的”。如果有对象既不被 GC Roots 引用,也无法通过 GC Roots 递归访问,那么这些对象就被认为是不可达的,可以被视为垃圾。 例如,如果程序中创建了一个独立的对象 `Object isolated = new Object();`,但没有将它赋值给任何变量,那么这个对象是不可达的,因为它与任何 GC Roots 都没有联系,垃圾回收器会将其回收。 ### 总结:为什么这些被称为 GC Roots 这些对象被称为 GC Roots,是因为它们: 1. **起始点角色**:GC Roots 是整个可达性分析的起点,从它们出发才能递归地找到所有存活的对象。 2. **持久性**:GC Roots 自身通常具有较长的生命周期(如静态变量、线程栈中的变量、本地引用等),在程序执行期间始终保留并持有关键的对象引用。 3. **全局可访问性**:GC Roots 可以被 JVM 全局访问,这样即使对象之间的引用关系复杂,也可以保证从 GC Roots 开始逐层递归标记所有可达对象,确保不会误删关键的存活对象。 因此,GC Roots 是垃圾回收过程的“根”,从这些根节点出发,垃圾回收器可以安全地标记所有活跃对象并清理无用对象。编辑于 2024-11-16 23:23 •IP属地 美国举报评论收藏
希望有人告诉你开心比对错更重要。李白在 Java 中,**GC Roots** 是一组特殊的对象,垃圾回收器从这些对象出发递归地查找所有可达的对象,以判断哪些对象存活。GC Roots 是垃圾回收过程中可达性分析的起点,通过它们可以安全地找到活跃对象并清理无用对象。 ### 什么东西可以当做 GC Roots? 1. **虚拟机栈(栈帧)中的局部变量** - 每个线程的栈帧中保存了当前方法调用的局部变量,这些变量包含对对象的引用。 - 当方法调用时,局部变量表会保存方法的局部对象引用,例如参数、临时变量等,这些引用直接保留在线程栈上,因此可以作为 GC Roots。 2. **方法区中的类静态变量** - 所有类的静态变量(使用 `static` 修饰的字段)在类加载时保存在方法区。 - 静态变量是全局可访问的,生命周期与类相同,且在类卸载前始终保持存活,因此 JVM 会将这些静态变量作为 GC Roots。 3. **方法区中的常量引用** - 运行时常量池中的常量(如字符串常量、`final` 常量等)在加载后存储在方法区并持久存在。 - 这些常量引用的对象(如字符串常量池中的字符串)只要被引用,就会被认为是 GC Roots,防止被回收。 4. **本地方法栈中的 JNI 引用** - 使用 JNI(Java Native Interface)调用的本地代码会存储引用到本地方法栈中,这些引用不在 Java 堆内,但 JVM 仍将其视为 GC Roots。 - 本地方法栈中的 JNI 引用是关键资源,需要在 GC 时被保留。 5. **活动线程** - 所有存活的线程对象本身被视为 GC Roots,因为线程对象在运行时始终存活。 - 线程生命周期结束之前,这些线程对象不会被回收,从而保证它们所引用的对象也不会被错误回收。 6. **Java 虚拟机内部引用** - JVM 自身内部使用的一些特殊对象引用,如系统类加载器、JNIFunctionTable 等也会被当作 GC Roots,以确保它们所依赖的对象存活。 7. **全局系统引用** - 一些全局变量,例如 `System.out`、`System.in` 和 `System.err`,会被 JVM 作为全局引用保留。 - 这些引用被默认视为 GC Roots,因为它们是整个应用运行期间的重要资源。 ### 为什么这些被称为 GC Roots? 这些对象被称为 GC Roots,因为它们是垃圾回收过程的“根节点”: 1. **起始点角色**:GC Roots 是可达性分析的起点,从它们出发递归地查找所有存活的对象。 2. **持久性和全局性**:GC Roots 通常具有较长的生命周期(静态变量、局部变量、JNI 引用等),它们直接或间接可达的对象会被认为是存活的。 3. **保证内存一致性**:通过 GC Roots,JVM 可以确保任何与之关联的对象链都是一致的,避免误删关键的存活对象,保证程序正确运行。 综上,GC Roots 是垃圾回收器分析可达对象的起点,从这些“根”出发,垃圾回收器可以安全地标记活跃对象,清理无用对象,确保内存空间的合理利用。发布于 2024-11-10 17:13 •IP属地 浙江举报评论收藏
公众号进入网站体验更佳

