硬核干货——《中小企业 AI 实战指南》免费下载! 了解详情
写点什么

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

评论

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

论文分享:利用对象存储进行高性能数据分析

Greptime 格睿科技

数据库 rust 对象存储 时序数据库

听懂一句AI实话,做成一件零售实事

脑极体

AI

起心动念 | 生成式 AI 开发实践系列的开端

亚马逊云科技 (Amazon Web Services)

开源 生成式人工智能

商汤科技日日新SenseNova 4.0发布:这⼀次,带来全新的AI体验!

Lily

内地与香港司法协助落实,对 Web3.0 行业有何影响?

TechubNews

@工控工程师,往这儿看!80%工控兄弟都在种草的宝藏应用,你知道几条?

AIRIOT

在线模型系统分布式异构计算演变-京东广告算法架构体系建设

京东零售技术

人工智能 机器学习 算法

容器化对数据库的性能有影响吗?

小猿姐

数据库 Kubernetes 容器化

关键词搜索拼多多商品列表数据接口Python

tbapi

拼多多API接口 关键词搜索拼多多API接口 拼多多商品列表数据接口

投资更好的管理会计系统,探索全面预算管理的奥秘

智达方通

管理会计 全面预算管理

河南省政务民生、本地生活、新闻等垂域应用启动鸿蒙原生应用开发

最新动态

完蛋!我把AI喂吐了!

有道技术团队

网易有道 AIGC Prompt QAnything

《UNIX环境高级编程(第3版)》PDF

程序员李木子

EigenLayer火的原因还有介绍

币离海

以太坊 EigenLayer 质押

Java 数据类型详解与类型转换技巧

小万哥

Java 程序人生 编程语言 软件工程 后端开发

Qt项目第二弹-文件加解密之RSA

springIce

rsa qt openssl

淘宝app端商品详情数据采集

tbapi

新增模型检验,指标自动评估模型效果|ModelWhale 版本更新

ModelWhale

人工智能 机器学习 大数据 数据分析 模型训练

2024-02-03:用go语言,你有 k 个背包。给你一个下标从 0 开始的整数数组 weights, 其中 weights[i] 是第 i 个珠子的重量。同时给你整数 k, 请你按照如下规则将所有

福大大架构师每日一题

福大大架构师每日一题

【AIOps】日志分析领域难题:如何对混合日志进行自动化解析?

人工智能 软件开发 华为云

专有云 ABC Stack 联合银联商务打造金融级云平台,入选《2024 央国企上云用云典型案例》

Baidu AICLOUD

专有云 金融云

【揭秘SAML协议 — Java安全认证框架的核心基石】 从初识到精通,带你领略Saml协议的奥秘,告别SSO的迷茫与困惑

码界西柚

SAML2.0 SSO 单点登录 后端技术 2024年第三十篇文章

想好新年去哪了吗?合合信息扫描全能王用AI“留住”年味

合合技术团队

文字识别 新年计划 图像处理 合合信息 扫描全能王

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