写点什么

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:004265

评论

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

【FAQ】HarmonyOS SDK 闭源开放能力 —Share Kit(2)

HarmonyOS SDK

harmoyos

VMware NSX 4.2.2 发布,新增功能概览

sysin

nsx

通义灵码入职表现实测:蔚来汽车AI 生成代码占比在 30% 以上

阿里巴巴云原生

阿里云 云原生 通义灵码

数安智用·科技强警|万里红依托“三大优势×五大能力”受邀参展第十二届警博会

新消费日报

Apple Safari 18.5 - macOS 专属浏览器 (独立安装包下载)

sysin

safari

A10 ACOS 6 - 专为现代应用程序设计的开放式云就绪操作系统

sysin

A10

重塑“DATA+AI“的共生范式:DataBuilder如何赋能企业数据价值跃迁

数造万象

人工智能 AI 数据 数据集 Data + AI

和鲸支持!南大人工智能通识课,让每个学生都懂AI

ModelWhale

他为SeaTunnel写下10+高质量PR,还把开源带进了公司生产线!

白鲸开源

源码交付+可控部署:用户行为分析系统的落地经验

ClkLog

开源 数据分析 埋点 用户行为分析 客户画像

AI for All,Code for All|七牛云 AI 开源项目扶持计划全面启动

七牛云

开源 AI

博云 AIOS 通过国家工信安全中心测试,产品完整性与功能性获权威认证

BoCloud博云

博云

国内到美国的网络问题怎么解决?美国专线网络方案详解

Ogcloud

SD-WAN 美国专线网络 美国网络专线 美国专线 中美网络专线

什么是区块链dapp开发?它能做什么?

区块链软件开发推广运营

交易所开发 dapp开发 链游开发 公链开发 代币开发

公链开发及其配套设施:钱包与区块链浏览器

区块链软件开发推广运营

交易所开发 dapp开发 链游开发 公链开发 公链开发代币开发

Shotcut 25.05 (Linux, macOS, Windows) - 免费开源视频编辑器

sysin

视频编辑

WhaleTunnel 信创数据库适配能力全景图:打通国产数据生态的最后一公里

白鲸开源

数据库 大数据 信创 白鲸开源 WhaleTunnel

重磅预告 | Apache SeaTunnel接入MCP,即将解锁模型上下文协议超能力!

白鲸开源

开源 AI 大模型 Apache SeaTunnel MCP

企业跨国组网怎么选?MPLS与SD-WAN方案对比

Ogcloud

企业组网 异地组网 跨国网络 国际网络专线 跨国网络专线

手把手教你抓取京东商品评论:API 接口解析与 Python 实战

tbapi

京东商品评论接口 京东API 京东商品评论API 京东评论接口 京东评论内容采集

通义灵码入职表现实测:蔚来汽车AI 生成代码占比在 30% 以上

阿里云云效

阿里云 云原生 通义灵码

Metasploit Framework 6.4.63 (macOS, Linux, Windows) - 开源渗透测试框架

sysin

Metasploit

A10 Thunder 6.0.5 - 应用交付与负载均衡

sysin

A10

文献解读-Sentieon DNAscope LongRead – A highly Accurate, Fast, and Efficient Pipeline for Germline Variant Calling from PacBio HiFi

INSVAST

长读长测序 Sentieon 变异分析 DNAscope LongRead 生信分析服务

A10 vThunder 6.0.5 - 虚拟化应用交付控制器 (ADC)

sysin

A10

AI题库软件系统的技术难点

北京木奇移动技术有限公司

软件外包公司 AI题库系统 题库软件系统

Metasploit Pro 4.22.7-2025051201 (Linux, Windows) - 专业渗透测试框架

sysin

Metasploit

天下拍-资产拍卖经典案例分享

至存网络

拍卖 拍卖系统 拍卖软件 艺术品拍卖 资产拍卖

去中心化云算力重构3A云游戏,元宇宙游戏还会远吗?

PowerVerse

元宇宙 云游戏 去中心化云算力

升级遇到坑?一文带你搞定DolphinScheduler 2.0到3.0升级

白鲸开源

开源 技术 干货 Apache DolphinScheduler 版本升级

AI题库APP的开发框架

北京木奇移动技术有限公司

题库系统 软件外包公司 AI题库

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