当前位置:首页 > 文章列表 > 文章 > java教程 > Java reachabilityFence为什么放在资源使用后的finally里

Java reachabilityFence为什么放在资源使用后的finally里

来源:17golang原创 2026-11-15 15:09:24 0浏览 收藏

Reference.reachabilityFence(owner) 放在资源使用之后,是为了让包装对象至少保持强可达直到这次同步使用结束;放进 finally,则让正常返回和异常离开都经过这个保护点。它避免的是因包装对象过早不再可达而触发自动清理,并不负责调用 close,也不能阻止其他线程显式关闭资源。

适合讨论的场景是:Java 对象保存一个外部资源句柄,Cleaner 根据包装对象的可达性兜底释放该资源。业务方法把句柄读到局部变量后,后续操作可能只需要这个数字;“方法还没返回”并不自动等于“包装对象仍然需要保持可达”。

Java 官方文档地址:https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/ref/Reference.html

明确需要保护的是资源包装对象

JLS 对可达性的讨论允许实现依据后续计算是否仍需要对象进行优化。局部变量在源码作用域内存在,并不表示对象一定会活到右花括号。对于普通纯内存对象,这通常没有可见影响;当可达性关联外部资源清理时,才需要认真处理这项约定。

可达性规范:https://docs.oracle.com/javase/specs/jls/se25/html/jls-12.html#jls-12.6.1

本文把资源包装对象称为 owner,把持有句柄的独立对象称为清理状态。它们的角色不同:清理状态还活着,不代表 owner 仍然强可达。fence 的参数应当是决定自动清理时机的那个 owner。

准备独立清理状态与资源所有权

先明确前提:同一个外部资源只有一个释放所有者;同步读取期间不允许其他线程显式 close;原生读取返回时已经停止使用资源。Cleaner 的清理动作不能反向持有被注册的包装对象,否则会破坏自动清理的可达性条件。

下面用 NativeBlock 展示结构。JNI读取和释放方法是集成点,需要真实原生库实现;示例没有提供资源分配器、JNI实现或运行结果,也不用于证明某次 GC 必然在某个位置发生。

import java.lang.ref.Cleaner;
import java.lang.ref.Reference;
import java.util.concurrent.atomic.AtomicLong;

// 结构示例:原生方法需由真实 JNI 库实现,本类不是独立可运行程序
final class NativeBlock implements AutoCloseable {
    private static final Cleaner CLEANER = Cleaner.create();

    // 静态清理状态只保存句柄,不反向引用 NativeBlock
    private static final class ReleaseState implements Runnable {
        private final AtomicLong handle;

        ReleaseState(long ownedHandle) {
            handle = new AtomicLong(ownedHandle);
        }

        long requireHandle() {
            long value = handle.get();
            if (value == 0) {
                throw new IllegalStateException("资源已经关闭");
            }
            return value;
        }

        @Override
        public void run() {
            long value = handle.getAndSet(0);
            if (value != 0) {
                // 桥接层约定释放操作短小且不抛异常
                freeNative(value);
            }
        }
    }

    private final ReleaseState state;
    private final Cleaner.Cleanable cleanable;

    // 调用方转交一个有效非零句柄的唯一释放责任
    NativeBlock(long ownedHandle) {
        if (ownedHandle == 0) {
            throw new IllegalArgumentException("句柄不能为零");
        }
        state = new ReleaseState(ownedHandle);
        cleanable = CLEANER.register(this, state);
    }

    int readByte() {
        try {
            long handle = state.requireHandle();
            // 该调用必须同步完成使用,不能在返回后继续持有资源
            return readNative(handle);
        } finally {
            // 保活的是被 Cleaner 注册的包装对象,不只是句柄状态
            Reference.reachabilityFence(this);
        }
    }

    @Override
    public void close() {
        // 确定性释放仍由调用方负责
        cleanable.clean();
    }

    private static native int readNative(long handle);
    private static native void freeNative(long handle);
}

ReleaseState 使用静态嵌套类,避免隐式捕获外层实例;非零句柄交给该状态管理,零作为已关闭标记。AtomicLong 只帮助记录关闭状态,不自动使“检查后再使用”与 close 互斥。这个限制直接决定示例的适用范围。

Cleaner 官方说明:https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/ref/Cleaner.html

NativeBlock包装对象、清理状态与原生句柄的静态所有权关系
图1:原创结构图,包装对象持有清理状态,清理动作独立保存句柄;清理状态不反向引用包装对象。

在同步调用后的 finally 放置 fence

readByte 的 try 中完成资源使用,finally 中对 this 调用 fence。这样保活的是 Cleaner 注册时关联的 NativeBlock,而不是局部 long 句柄。如果读取抛出异常,finally 仍按 Java 的异常处理规则执行。

抽去业务细节后,所需结构如下。它是放置位置的片段,operation 与 owner 都要由实际类提供。

// 仅展示方法内的放置结构;operation 必须在返回前结束资源使用
try {
    operation();
} finally {
    // owner 是决定自动清理何时可以发生的包装对象
    Reference.reachabilityFence(owner);
}

不要把 fence 当成一次“刷新GC状态”的动作,也不要认为调用后对象立刻被回收。Reference 文档明确说明该方法本身不启动垃圾回收、引用处理或终结操作;它提供的是可达性保证。

方法说明:https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/ref/Reference.html#reachabilityFence(java.lang.Object)

理解异常路径与放错位置的差别

写法保护边界问题
先 fence 再使用资源保证只到前面的保护点后续使用没有因此获得保活保证
使用后普通语句调用 fence只覆盖走到该语句的路径此前异常离开时可能跳过
使用后的 finally 中调用覆盖正常与异常离开路径仍需满足同步使用及关闭约定
对清理状态而非 owner 调用保活了另一个对象未必保护 Cleaner 注册对象

finally 是为了覆盖控制路径;准确的保护对象与覆盖范围同样重要。若方法在另一层已经保证 owner 始终强可达,则不必机械地为每个普通Java方法添加 fence。

可达性保护、显式关闭和异步使用的责任边界
图2:原创说明图,fence、显式close与异步任务各有责任范围,图中连线表达归属而不是执行顺序。

另外处理 close 并发与异步资源使用

最容易过度推断的是“有了fence,资源访问就线程安全”。上面的 requireHandle 可能先读到非零值,另一线程随后执行 close,再由读取线程调用原生方法。对象此时完全可以仍然强可达,因此 fence 无法排除这种显式关闭竞态。

如果业务允许并发 close,需要另外设计互斥、读写锁或资源租约,让操作从获取使用权到释放使用权期间都受到保护。仅把句柄换成原子变量不能完成这件事。

异步任务也不能照搬同步方法的边界。原生接口若在返回后继续使用句柄,方法出口的 fence 结束得太早;应让 owner 或明确的租约覆盖任务真正结束、取消和失败清理的路径。任务提交成功不等于资源使用完成。

安排阅读检查与清理责任

把这类代码接入项目时,先确认四件事:fence的参数是否是被清理的包装对象;所有同步资源使用是否都位于保护点之前;异常路径是否经过finally;显式关闭是否由另一套生命周期协议控制。本文提供的是接入时的阅读检查项,没有执行代码或GC复现。

正常业务仍应通过 AutoCloseable 的确定性关闭方式释放资源,Cleaner只承担兜底角色。资源分配失败、注册失败和原生释放失败的处理应按桥接库约定补齐,不能把此处的结构示例当成完整生产级原生资源管理库。

是否可以用 System.gc() 检查 fence 是否生效?单次强制GC请求和日志先后不能证明所有优化场景,不能把偶然未复现当成生命周期正确性依据。

fence 是否可以替代 try-with-resources?不可以。前者处理可达性保证,后者表达离开作用域时关闭资源的责任。

只保存句柄到局部变量够不够?句柄值仍在使用,不一定意味着 owner 仍被需要。需要保护的是控制清理时机的对象,而不是只保留一个数字。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Java SwitchPoint失效后为什么不能重新启用Java SwitchPoint失效后为什么不能重新启用
上一篇
Java SwitchPoint失效后为什么不能重新启用
Java Stream按组取最大元素
下一篇
Java Stream按组取最大元素
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    1038次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    1037次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    1181次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    1141次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    985次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码