Java垃圾收集之对象存活判定、回收流程与内存策略详解

 更新时间:2026年03月08日 10:28:07   作者:钮祜禄爱因斯晨  
Java对象的内存分配主要发生在堆内存(新生代、老年代),少数情况可能分配在栈(栈上分配)或直接内存(堆外内存),这篇文章主要介绍了Java垃圾收集之对象存活判定、回收流程与内存策略的相关资料,需要的朋友可以参考下

一、引言

在 Java 技术体系里,垃圾收集器(Garbage Collection,GC)与内存分配策略是自动内存管理的核心支撑。深入探究其原理与机制,对优化程序内存性能、规避内存泄漏与溢出等问题意义重大,是理解 Java 运行时环境的关键环节

二、GC 基础与核心问题

(一)GC 概念溯源

垃圾收集技术并非 Java 首创,早在 1960 年,Lisp 语言已应用内存动态分配与垃圾收集。GC 需解决三个核心问题:

  1. 识别待回收内存:确定哪些对象已 “死亡”,即无被使用可能。
  2. 抉择回收时机:依据内存使用状况,选择合适时机触发回收。
  3. 选定回收方式:不同垃圾收集器采用各异的回收算法与实现逻辑 。

(二)对象存活判定算法

1. 引用计数算法

原理为给对象绑定引用计数器,引用建立时计数器加 1,引用失效时减 1,计数器为 0 则判定对象可回收。但存在循环引用缺陷,如代码所示:

public class ReferenceCountingGC {
    public Object instance = null;
    private static final int _1MB = 1024 * 1024;
    private byte[] bigSize = new byte[2 * _1MB];
    public static void testGC() {
        ReferenceCountingGC objA = new ReferenceCountingGC();
        ReferenceCountingGC objB = new ReferenceCountingGC();
        objA.instance = objB;
        objB.instance = objA;
        objA = null;
        objB = null;
        System.gc();
    }
}

objAobjB相互引用,虽无实际访问路径,但引用计数不为 0,算法无法回收,故主流 Java 虚拟机弃用该算法。

2. 可达性分析算法

主流商用语言(如 Java、C#)采用的对象存活判定算法。以 “GC Roots” 为起始节点集,依据引用关系遍历搜索,无引用链连接的对象判定为可回收。Java 中,GC Roots 涵盖:

  • 虚拟机栈本地变量表引用对象,如方法内参数、局部变量。
  • 方法区静态属性与常量引用对象,像类的静态变量、字符串常量池引用。
  • 本地方法栈 JNI(Native 方法)引用对象。
  • 虚拟机内部核心引用,包括 Class 对象、常驻异常对象(如 NullPointerException )等 。

三、引用分类及特性(JDK 1.2+)

JDK 1.2 拓展引用概念,按强度分为四类,各有独特内存管理行为:

(一)强引用

程序中最常见,如 Object obj = new Object() 。只要引用有效,对象不会被回收,是对象强存活的保障,支撑程序基本对象引用逻辑。

(二)软引用

用于描述非必需但仍具使用价值的对象,内存不足即将抛出溢出异常前,会触发软引用对象回收。通过 SoftReference 实现,代码示例:

SoftReference<Object> softRef = new SoftReference<>(new Object());
Object obj = softRef.get(); 

适用于缓存场景,内存紧张时释放非必需对象,平衡内存使用与功能需求。

(三)弱引用

强度弱于软引用,垃圾收集时,无论内存是否充足,弱引用关联对象都会被回收。借助 WeakReference 实现:

WeakReference<Object> weakRef = new WeakReference<>(new Object());
Object obj = weakRef.get(); 

常用于弱关联对象管理,如观察者模式中临时关联,避免因对象弱引用导致内存无法释放。

(四)虚引用

又称幽灵 / 幻影引用,不影响对象生命周期,也无法通过其获取对象,主要用于接收对象回收系统通知,由 PhantomReference 实现。是内存回收事件监听的特殊手段,可在对象回收时执行特定资源清理等操作 。

四、对象回收流程与机制

对象经可达性分析判定为不可达后,需历经两次标记才会被回收:

(一)首次标记与筛选

对象无 GC Roots 引用链时,触发首次标记。随后筛选是否需执行 finalize() 方法,未重写该方法或方法已执行过的对象,直接判定为可回收。

(二)二次标记与挽救

需执行 finalize() 的对象,被移入F - Queue队列,由 Finalizer 线程执行该方法。若对象在 finalize() 中重新建立引用链(如关联到类变量 ),二次标记时会被移出回收队列;否则,执行回收。但 finalize() 存在运行代价高、不确定性大、无法保证调用顺序等问题,推荐以 try - finally 替代,示例代码展现对象自我拯救过程:

public class FinalizeEscapeGC {
    public static FinalizeEscapeGC SAVE_HOOK = null;
    public void isAlive() {
        System.out.println("yes, i am still alive :)");
    }
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize method executed!");
        FinalizeEscapeGC.SAVE_HOOK = this;
    }
    public static void main(String[] args) throws Throwable {
        // 首次拯救逻辑
        SAVE_HOOK = new FinalizeEscapeGC();
        SAVE_HOOK = null;
        System.gc();
        Thread.sleep(500); 
        if (SAVE_HOOK != null) {
            SAVE_HOOK.isAlive();
        } else {
            System.out.println("no, i am dead :(");
        }
        // 二次拯救(失败,因 finalize 仅执行一次)
        SAVE_HOOK = null;
        System.gc();
        Thread.sleep(500); 
        if (SAVE_HOOK != null) {
            SAVE_HOOK.isAlive();
        } else {
            System.out.println("no, i am dead :(");
        }
    }
}

五、方法区的回收机制

方法区(如 HotSpot 的元空间 / 永久代 )常被误解为无垃圾收集,实则不然,其回收聚焦以下两部分:

(一)常量池回收

若常量池中的常量(如字符串 )无对象引用,且虚拟机无其他引用,可被回收。如字符串 “java” ,若系统无对应引用,内存回收时可能被清理,优化常量池内存占用。

(二)无用类回收

判定类为 “无用” 需满足三个条件:

  1. 类及派生子类无实例,Java 堆中不存在该类相关实例。
  2. 加载该类的类加载器被回收,此条件在自定义类加载器场景中较难满足,需精心设计。
  3. 该类对应的 java.lang.Class 对象无引用,无法通过反射访问类方法。

Java 虚拟机允许回收满足条件的类,但非强制。在反射、动态代理等场景,需虚拟机具备类型卸载能力,可通过 -verbose:class-XX:+TraceClassLoading-XX:+TraceClassUnLoading(部分需 FastDebug 版支持 )查看类加载 / 卸载信息,保障方法区内存健康。

六、结论

Java 垃圾收集器与内存分配策略,构建起自动内存管理的核心体系。从对象存活判定的算法演进,到引用分类的精细管控,再到对象回收流程的严谨执行与方法区回收的特殊处理,共同保障程序内存高效利用。深入理解该体系,是优化程序性能、解决内存问题的关键。后续将进一步探究具体垃圾收集器(如 Serial、G1 等 )的实现与内存分配策略细节,持续深化 Java 内存管理研究,为 Java 开发者筑牢技术根基,助力打造更高效、稳定的Java应用 。

到此这篇关于Java垃圾收集之对象存活判定、回收流程与内存策略的文章就介绍到这了,更多相关Java对象存活判定、回收流程与内存策略内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • spring-cloud入门之eureka-server(服务发现)

    spring-cloud入门之eureka-server(服务发现)

    本篇文章主要介绍了spring-cloud入门之eureka-server(服务发现),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01
  • Spring Boot 中 Properties 与 YAML 的深度解析与最佳实践记录(告别配置混乱)

    Spring Boot 中 Properties 与 YAML&nbs

    Spring配置用于管理组件和依赖,SpringBoot支持多种格式,默认使用application.properties或yml,Props简单但冗余,YAML结构清晰支持复杂数据,格式严格,建议统一配置类型避免冲突,本文介绍Spring Boot 中 Properties 与 YAML 的深度解析与最佳实践,感兴趣的朋友一起看看吧
    2025-07-07
  • Java中Object类的理解和使用

    Java中Object类的理解和使用

    Object类是java.lang包下的核心类,Object类是所有类的父类,何一个类时候如果没有明确的继承一个父类的话,那么它就是Object的子类,本文将通过代码示例详细介绍一下Java中Object类的理解和使用,需要的朋友可以参考下
    2023-06-06
  • spring boot RestTemplate 发送get请求的踩坑及解决

    spring boot RestTemplate 发送get请求的踩坑及解决

    这篇文章主要介绍了spring boot RestTemplate 发送get请求的踩坑及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • 深入理解Java中的SPI机制

    深入理解Java中的SPI机制

    这篇文章主要介绍了深入理解Java中的SPI机制,帮助大家更好的理解和学习使用Java,感兴趣的朋友可以了解下
    2021-02-02
  • 多线程计数,怎么保持计数准确的方法

    多线程计数,怎么保持计数准确的方法

    这篇文章主要介绍了多线程计数的方法,有需要的朋友可以参考一下
    2014-01-01
  • SpringBoot中@RequestBody的伪表单提交场景

    SpringBoot中@RequestBody的伪表单提交场景

    @RequestBody是Spring MVC/Spring Boot中用于处理HTTP请求体数据的核心注解,主要用于将JSON/XML格式的请求体数据自动绑定到方法参数对象上, 下面就来介绍一下如何使用
    2025-12-12
  • 基于Properties文件中的空格问题

    基于Properties文件中的空格问题

    这篇文章主要介绍了Properties文件中的空格问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • 简单了解Java中的可重入锁

    简单了解Java中的可重入锁

    这篇文章主要介绍了简单了解Java中的可重入锁,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11
  • Java实现线程安全单例模式的五种方式的示例代码

    Java实现线程安全单例模式的五种方式的示例代码

    这篇文章主要介绍了Java中实现线程安全单例模式的五种方式:饿汉式、枚举单例、懒汉式、DCL懒汉式和静态内部类懒汉单例,感兴趣的可以了解一下
    2022-03-03

最新评论