写点什么

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

评论

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

什么是NineData?突然就火了

数据库小组

数据库 数据备份 多云管理 跨云数据

佛萨奇2.0项目系统开发源代码(可多链部署搭建)

开发微hkkf5566

Navicat!OceanBase社区版新朋友来啦!

OceanBase 数据库

数据库 oceanbase

内测分享:如何把小程序游戏运行到自有 App 中?(IOS 篇)

FN0

游戏 小程序游戏开发

事关紧急!每个运维人员都应该了解的工作

嘉为蓝鲸

自动化运维 嘉为蓝鲸 AD防护

跟我学Python图像处理丨图像分类原理与案例

华为云开发者联盟

Python 人工智能 华为云 12 月 PK 榜

海量数据下查询慢、数据不一致难题如何解?看某游戏公司的技术实践

OceanBase 数据库

数据库 oceanbase

React组件复用的发展史

夏天的味道123

React

ha集群是什么意思?一般有几个节点?

行云管家

高可用 ha ha集群

2023年北京企业办理备案流程详解

行云管家

等保 等保备案 北京

React核心工作原理

xiaofeng

React

React组件复用的技巧

夏天的味道123

React

还在写代码处理映射关系? 动动手指,MatrixDB 4.4 轻松让 JSON 入表!

YMatrix 超融合数据库

kafka 产品发布 超融合数据库 YMatrix MatrixGate

手把手教你成为荣耀开发者:数据报表使用指引

荣耀开发者服务平台

android 手机 荣耀 honor

高性能数据访问中间件 OBProxy(七):安全、协议和监控

OceanBase 数据库

数据库 oceanbase

华为云发布CodeArts Req需求管理工具,让需求管理化繁为简

华为云开发者联盟

云计算 华为云 12 月 PK 榜

保驾护航,嘉为蓝鲸助力某科技公司AD域故障恢复实记

嘉为蓝鲸

AD 自动化运维 嘉为蓝鲸

React高级特性之Context

夏天的味道123

React

《中国指标中台市场研究报告》线上发布会

Kyligence

数据分析 指标中台

人保科技:自动化运维场景在数据中心的落地之网络策略自动化管理

嘉为蓝鲸

自动化管理 自动化运维 嘉为蓝鲸

Vue中的diff算法深度解析

yyds2026

Vue

Vue3知识点之数据侦测

yyds2026

Vue

5年经验年薪百万,一位阿里P8分享自己的成长干货

钟奕礼

Java java面试 java编程 程序员、

React组件通信

xiaofeng

React

Vue3必会技巧-自定义Hooks

yyds2026

Vue

ui设计网站:全网最热门的30个UI设计网站合集

uimaker

UI GUI设计

React组件设计模式-纯组件,函数组件,高阶组件

xiaofeng

React

1业务架构作业

梁山伯

龙蜥大讲堂12月预告来了!来自统信软件、移动云和浪潮信息等多位技术专家在线分享

OpenAnolis小助手

ebpf 移动云 龙蜥大讲堂 直播预告 统信软件

Java程序员开发3年如何能拿到年薪40W?(强烈推荐涨薪必备)

钟奕礼

Java 程序员 java面试 java编程

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