JDK23将弃用 sun.misc.Unsafe中的内存访问方法并将其删除

类sun.misc.Unsafe于 2002 年推出,是 JDK 中 Java 类执行底层操作的一种方式。其大多数方法(87 种方法中的 79 种)用于访问内存,在JDK23中将通过VarHandle API(JEP 193,JDK 9)和 Foreign Function & Memory API(JEP 454,JDK 22)来彻底替换原来通过sun.misc.Unsafe实现的内存访问方法。

1.原因

基于类sun.misc.Unsafe的访问内存操作,无论是 JVM 的垃圾收集堆还是堆外内存,这些内存都不受 JVM 控制。正如该类的名称所暗示的那样,这些内存访问方法是不安全的:它们可能导致未定义的行为,包括 JVM 崩溃。因此,它们没有作为标准 API 公开。它们既不是为广泛的客户端设计的,也不是永久的。相反,它们是在假设它们专用于 JDK 的情况下引入的,并且 JDK 中的调用者在使用它们之前会执行详尽的安全检查,并且最终会将此功能的安全标准 API 添加到 Java 平台中。

然而,由于在 2002 年无法阻止sun.misc.Unsafe在 JDK 之外使用,其内存访问方法成为了库开发人员的得力工具,他们希望获得比标准 API 更强大的功能和性能。例如,sun.misc.Unsafe::compareAndSwap可以在没有java.util.concurrent.atomic API 开销的情况下对字段执行 CAS(比较和交换)操作,而sun.misc.Unsafe::setMemory可以操作堆外内存而不受 2GB 的限制。依赖于ByteBuffer操作堆外内存的java.nio.ByteBuffer库(如 Apache Hadoop 和 Cassandra),sun.misc.Unsafe::invokeCleaner通过及时释放堆外内存来提高效率。

不幸的是,并非所有库都会在调用内存访问方法之前认真执行安全检查,因此应用程序存在故障和崩溃的风险。其他方法的使用可能会导致 JVM 禁用优化,从而导致性能比使用普通 Java 数组更差。尽管如此,由于内存访问方法的使用非常广泛,因此在 JDK 9( JEP 260sun.misc.Unsafe )中没有与其他底层 API 一起封装。它在 JDK 22 中仍然开箱即用,等待安全支持的替代方案的出现。

2.过度阶段如何使用sun.misc.Unsafe类

  • –sun-misc-unsafe-memory-access=allow允许使用内存访问方法,运行时不会出现任何警告。

  • –sun-misc-unsafe-memory-access=warn允许使用内存访问方法,但第一次使用任何内存访问方法(无论是直接使用还是通过反射)时都会发出警告。也就是说,无论使用哪种内存访问方法以及使用任何特定方法多少次,最多都会发出一次警告。

  • –sun-misc-unsafe-memory-access=debug允许使用内存访问方法,但每次使用任何内存访问方法(无论是直接使用还是通过反射)都会发出一行警告和堆栈跟踪。

  • –sun-misc-unsafe-memory-access=denyUnsupportedOperationException通过在每次使用此类方法时(无论是直接使用还是通过反射)抛出一个来禁止使用内存访问方法。

3.sun.misc.Unsafe内存访问方法的替代方案

3.1.访问堆上内存(on-heap)的方法

3.1.1.原有sun.misc.Unsafe方法

3.1.1.1.变量

int INVALID_FIELD_OFFSET
int ARRAY_[TYPE]BASE_OFFSET
int ARRAY
[TYPE]_INDEX_SCALE

3.1.1.2.方法
  • long objectFieldOffset(Field f)
  • long staticFieldOffset(Field f)
  • Object staticFieldBase(Field f)
  • int arrayBaseOffset(Class<?> arrayClass)
  • int arrayIndexScale(Class<?> arrayClass)

这些方法用于获取偏移量或比例,然后将其与双峰方法(见下文)一起使用来读取和写入字段或数组元素。

3.1.2.替代方法

这些用例现在由MemorySegment::ofArray和VarHandle替代。在极少数情况下,这些方法会单独用于检查和操作内存中对象的物理布局。此用例没有支持的替代方案

3.2.访问堆外内存(off-heap)的方法

3.2.1.原有sun.misc.Unsafe方法

  • 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)
  • long getAddress(long address)
  • void putAddress(long address, long x)
  • int addressSize() (and int ADDRESS_SIZE)

3.2.2.现有方法

  • Arena::allocate or an FFM downcall to the C library’s malloc()
  • Downcall to realloc()
  • Arena::close or downcall to free()
  • MemorySegment::asByteBuffer
  • MemorySegment::fill
  • MemorySegment::copy
  • MemorySegment.get(ValueLayout.Of[Type] layout, long offset)
  • MemorySegment.set(ValueLayout.of[Type] layout, long offset, [type] value)
  • MemorySegment.get(ValueLayout.OfAddress layout, long offset)
  • MemorySegment.set(ValueLayout.ofAddress layout, long offset, MemorySegment value)
  • ValueLayout.ADDRESS.byteSize()

3.3.用于访问堆上和堆外内存的方法(双峰 - 双峰方法采用一个参数,该参数要么引用堆上的对象,要么为空以表示堆外访问)。

3.3.1.原有sun.misc.Unsafe方法

  • [type] get[Type](Object o, long offset)
  • void put[Type](Object o, long offset, [type] x)
  • [type] get[Type]Volatile(Object o, long offset)
  • void put[Type]Volatile(Object o, long offset, [type] x)
  • void putOrdered[Type](Object o, long offset, [type] x)
  • [type] getAndAdd[Type](Object o, long offset, [type] delta)
  • [type] getAndSet[Type](Object o, long offset, [type] newValue)
  • boolean compareAndSwap[Type](Object o, long offset, [type] expected, [type] x)
  • void setMemory(Object o, long offset, long bytes, byte value)
  • void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes)

3.3.2.现有方法

  • VarHandle::get
  • VarHandle::set
  • VarHandle::getVolatile
  • VarHandle::setVolatile
  • VarHandle::setRelease
  • VarHandle::getAndAdd
  • VarHandle::getAndSet-
  • VarHandle::compareAndSet
  • MemorySegment::fill or Arrays::fill
  • MemorySegment::copy or System::arrayCopy

4.sun.misc.Unsafe内存访问方法的替代示例

4.1.访问堆上内存(on-heap)的方法

4.1.1.需求1

假设类Foo中有一个int字段,我们希望将其原子化为两倍。

4.1.1.1.sun.misc.Unsafe的实现源码
class Foo {
   

    private static final Unsafe UNSAFE = ...;    // A sun.misc.Unsafe object

    private static final long X_OFFSET;

    static {
   
        try {
   
            X_OFFSET = UNSAFE.objectFieldOffset(Foo.class.getDeclaredField("x"))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值