写点什么

JEP 456:准备删除 Unsafe 中的内存访问方法

  • 2024-07-10
    北京
  • 本文字数:2964 字

    阅读完需:约 10 分钟

JEP 456:准备删除 Unsafe 中的内存访问方法

JEP 471(弃用 sun.misc.Unsafe 中的内存访问方法以备删除)已经在 JDK 23 中发布。该 JEP 建议弃用 Unsafe 类中的内存访问方法,以便在将来的版本中删除。这些不再支持的方法已经被标准 API 所取代:JEP 193(变量句柄,已在 JDK 9 中交付)和 JEP 454(外部函数和内存 API,已在 JDK 22 中交付)。


弃用这些方法的主要目的是为最终删除sun.misc.Unsafe中的内存访问方法做准备。编译时和运行时警告会突出显示这些方法的使用情况,开发人员可以借此识别并迁移到受支持的替代方法。这一转变的目标是确保应用程序能够顺利过渡到现代 JDK 版本,从而增强安全性和性能。


现在,有两个标准 API 为sun.misc.Unsafe提供了安全高效的替代方案。VarHandle API(即在 JDK 9 中交付的 JEP 193)提供了安全操作堆内存的方法,可以确保操作有效执行并且不会出现未定义的行为。外部函数和内存 API(即在 JDK 22 中交付的 JEP 454)提供了安全的堆外内存访问方法,通常与 VarHandle 搭配使用来管理 JVM 堆内和堆外内存。这些 API 承诺:不会出现未定义的行为、长期稳定以及更好地与 Java 工具和文档集成。


已弃用的sun.misc.Unsafe方法分为三类:堆内、堆外和双模(可以访问堆内和堆外内存的方法)。堆内方法包括:


long objectFieldOffset(Field f)long staticFieldOffset(Field f)Object staticFieldBase(Field f)int arrayBaseOffset(Class<?> arrayClass)int arrayIndexScale(Class<?> arrayClass)
复制代码


这些方法可以用VarHandleMemorySegment::ofArray及其重载方法代替。例如,考虑下面的例子:


class Foo {
private static final Unsafe UNSAFE = ...; // 一个 sun.misc.Unsafe 对象
private static final long X_OFFSET;
static { try { X_OFFSET = UNSAFE.objectFieldOffset(Foo.class.getDeclaredField("x")); } catch (Exception ex) { throw new AssertionError(ex); } }
private int x;
public boolean tryToDoubleAtomically() { int oldValue = x; return UNSAFE.compareAndSwapInt(this, X_OFFSET, oldValue, oldValue * 2); }}
复制代码


上述代码可以用 VarHandle 实现如下:


class Foo {
private static final VarHandle X_VH;
static { try { X_VH = MethodHandles.lookup().findVarHandle(Foo.class, "x", int.class); } catch (Exception ex) { throw new AssertionError(ex); } }
private int x;
public boolean tryAtomicallyDoubleX() { int oldValue = x; return X_VH.compareAndSet(this, oldValue, oldValue * 2); }}
复制代码


堆外方法主要有以下这些:


long allocateMemory(long bytes)long reallocateMemory(long address, long bytes)void freeMemory(long address)void invokeCleaner(java.nio.ByteBuffer directBuffer)void setMemory(long address, long bytes, byte value)void copyMemory(long srcAddress, long destAddress, long bytes)[type] get[Type](long address)void put[Type](long address, [type] x)
复制代码


这些方法可以用MemorySegment 操作替换。考虑下面的例子:


class OffHeapIntBuffer {
private static final Unsafe UNSAFE = ...;
private static final int ARRAY_BASE = UNSAFE.arrayBaseOffset(int[].class); private static final int ARRAY_SCALE = UNSAFE.arrayIndexScale(int[].class);
private final long size; private long bufferPtr;
public OffHeapIntBuffer(long size) { this.size = size; this.bufferPtr = UNSAFE.allocateMemory(size * ARRAY_SCALE); }
public void deallocate() { if (bufferPtr == 0) return; UNSAFE.freeMemory(bufferPtr); bufferPtr = 0; }
private boolean checkBounds(long index) { if (index < 0 || index >= size) throw new IndexOutOfBoundsException(index); return true; }
public void setVolatile(long index, int value) { checkBounds(index); UNSAFE.putIntVolatile(null, bufferPtr + ARRAY_SCALE * index, value); }
public void initialize(long start, long n) { checkBounds(start); checkBounds(start + n-1); UNSAFE.setMemory(bufferPtr + start * ARRAY_SCALE, n * ARRAY_SCALE, 0); }
public int[] copyToNewArray(long start, int n) { checkBounds(start); checkBounds(start + n-1); int[] a = new int[n]; UNSAFE.copyMemory(null, bufferPtr + start * ARRAY_SCALE, a, ARRAY_BASE, n * ARRAY_SCALE); return a; }
}
复制代码


上述代码使用标准 API ArenaMemorySegment 替换后如下:


class OffHeapIntBuffer {
private static final VarHandle ELEM_VH = ValueLayout.JAVA_INT.arrayElementVarHandle();
private final Arena arena; private final MemorySegment buffer;
public OffHeapIntBuffer(long size) { this.arena = Arena.ofShared(); this.buffer = arena.allocate(ValueLayout.JAVA_INT, size); }
public void deallocate() { arena.close(); }
public void setVolatile(long index, int value) { ELEM_VH.setVolatile(buffer, 0L, index, value); }
public void initialize(long start, long n) { buffer.asSlice(ValueLayout.JAVA_INT.byteSize() * start, ValueLayout.JAVA_INT.byteSize() * n) .fill((byte) 0); }
public int[] copyToNewArray(long start, int n) { return buffer.asSlice(ValueLayout.JAVA_INT.byteSize() * start, ValueLayout.JAVA_INT.byteSize() * n) .toArray(ValueLayout.JAVA_INT); }
}
复制代码


迁移将分几个阶段进行,每个阶段对应一个单独的 JDK 版本。在第一阶段(从 JDK 23 开始),所有内存访问方法都将被弃用,并且将发出编译时警告。第二阶段(计划从 JDK 25 或更早的版本开始)将在发现使用已弃用方法的情况时发出运行时警告。第三阶段(计划从 JDK 26 或更高的版本开始)将进一步升级响应,在发现对这些方法的调用时默认抛出异常。最后,第四和第五阶段将删除已弃用的方法。这两个阶段可能发生在同一版本中。开发人员可以使用新增的命令行选项--sun-misc-unsafe-memory-access={allow|warn|debug|deny}来管理弃用警告并评估对其应用程序的影响。


弃用sun.misc.Unsafe内存访问方法是增强 Java 平台完整性和安全性的一个重要步骤。借助 VarHandle 及外部函数和内存 API,开发人员可以保证其应用程序的健壮性,并兼容未来的 JDK 版本。这种分阶段的方法为迁移提供了充足的时间,既有助于 Java 开发最佳实践的推广,又能够尽可能地减少由此带来的影响。


原文链接:

https://www.infoq.com/news/2024/06/jep-456-removing-unsafe-methods/

2024-07-10 08:004356

评论

发布
暂无评论
发现更多内容

KubeCon China 2025 | 与KubeEdge畅聊毕业经验与创新未来

华为云原生团队

云计算 容器 云原生

华为仓颉语言初识:并发编程之同步机制(下)

IT小码哥

鸿蒙 HarmonyOS HarmonyOS NEXT

低代码与AI的医药行业适配性实践:从技术融合到数智化转型的深度解析

量贩潮汐·WholesaleTide

低代码

扎根业务,这个618商业智能体先跑起来

脑极体

AI

商品中心—B端建品和C端缓存的技术文档(二)

电子尖叫食人鱼

架构

秒懂 AI Agent:落地局限与产品解析

Techinsight

智能体 agent 智能体评估

Dify实战案例:MySQL查询助手!嘎嘎好用

王磊

ETL中图表统计分析模版组件使用

谷云科技RestCloud

数据处理 ETL 数据集成 图表统计

不是做大模型,而是落在执行闭环,酷开智能体思路浮出水面

业界

TortoiseSVN使用-权限配置

刘大猫

人工智能 svn 权限控制 tortoiseSVN 权限配置

2025 深度洞察:AI Agent 落地的短板有哪些

Techinsight

一体系数据平台的进化:基于阿里云 EMR Serverless Spark的持续演进

阿里云大数据AI技术

云计算 Serverless 数据分析 数据处理 数据库 大数据

2025年Java面试行情变了?技术不达标,如何快速逆袭拿高薪?

程序员高级码农

Java Java 面试 Java 面试题

全议程硬核公布!龙蜥邀您共同探索面向泛在智算场景的操作系统技术

OpenAnolis小助手

AI 操作系统 龙蜥社区 龙蜥meetup

立足数字人文,深化历史叙事|科学智能赋能人文社科领域研究

ModelWhale

科学智能 人文社科 山东大学 AI4S

NJet Portal 应用门户管理介绍

通明湖

打造多模态交互新范式|彩讯股份中标2025年中国移动和留言平台AI智能体研发项目

彩讯股份300634

通义灵码带你玩转 MCP 最佳实践(合辑,持续更新中)

阿里云云效

通义灵码 MCP

西南会议 | 2025 Altair 区域技术交流会即将在成都盛大举行!

Altair RapidMiner

人工智能 AI HPC 数字孪生 CAE

AI时代的“数据之困”,什么是AI-Ready Data

MatrixOrigin

数据库 HTAP AI Agents

三类不同身份的“开发者”,在昇腾CANN的土壤上种下了繁荣之花

Alter

龙蜥开发者说:我的龙蜥开源之旅 | 第 32 期

OpenAnolis小助手

龙蜥社区 龙蜥开发者说

通义灵码带你玩转 MCP 最佳实践(合辑,持续更新中)

阿里巴巴云原生

通义灵码 MCP

京东高薪“抢人”,刘强东杀向携程腹地

趣解商业

京东 携程 酒旅

英特尔推进技术创新,以规模更大的封装满足AI应用需求

E科讯

五分钟搭建属于你的AI助手:Ollama+DeepSeek+AnythingLLM深度整合教程

量贩潮汐·WholesaleTide

人工智能 DeepSeek

技术视角:AI Agent 落地局限与产品实现

Techinsight

智能体 Agents AI 智能体 智能体评估

你了解Java线程池原理吗?

不在线第一只蜗牛

Java 算法

鸿蒙 NEXT 应用签名机制有何变化?如何生成并配置签名证书?

巴库一郎

鸿蒙 开发工具 HarmonyOS HarmonyOS NEXT 实践分享

iOS 26 新增实时翻译:基于端侧并向第三方开放接口;Neuralink 和 Grok 使渐冻症患者重新「发声」丨日报

声网

揭秘:AI Agent落地难在哪,产品又咋样?

Techinsight

智能体 AI 智能体

JEP 456:准备删除 Unsafe 中的内存访问方法_编程语言_A N M Bazlur Rahman_InfoQ精选文章