首页 > 其他分享 >Unsafe入门讲解

Unsafe入门讲解

时间:2024-08-20 10:08:45浏览次数:13  
标签:Compare 入门 int Unsafe misc 内存 讲解 方法

概述

作为一个8年多Javaer,曾无数次看到Unsafe这个类,但一直没有去翻过源码,此为背景。

借助于IDEA查看JDK源码,却发现有两个Unsafe类:

  • sun.misc.Unsafe
  • jdk.internal.misc.Unsafe

jdk.internal.misc.Unsafe和sun.misc.Unsafe

作为JDK中的两个不同类,它们提供一些底层的、非标准化的功能,用于直接操作内存和线程。

sun.misc.Unsafe
最初引入的类,提供一些低级别的操作,如直接内存访问、线程调度、CAS(Compare-And-Swap)操作等。功能强大,但也非常危险,绕过Java的安全机制,直接操作内存,容易导致崩溃或安全漏洞。

在早期的JDK中,许多框架和库(如juc包中的类)依赖于sun.misc.Unsafe来实现高效的并发控制和内存操作。然而,sun.misc.Unsafe是一个非标准API,并不是Java标准库的一部分,使用它的代码在不同的JDK版本之间可能不兼容。为了减少这种不兼容性,JDK开发者逐渐将对sun.misc.Unsafe的依赖移除或替换。

jdk.internal.misc.Unsafe
为了应对sun.misc.Unsafe的非标准化问题,JDK 9引入模块化系统(Project Jigsaw),并将一些内部API移动到jdk.internal包中。jdk.internal.misc.Unsafesun.misc.Unsafe的替代品,提供类似功能,但位于一个内部的、更明确的命名空间中。

区别:

  • 命名空间和访问控制:jdk.internal.misc.Unsafe位于jdk.internal模块中,使用更严格的访问控制。只有在明确声明模块依赖的情况下才能访问,而sun.misc.Unsafe则是一个开放的、非标准API
  • API变化:虽然两者提供的功能类似,但它们的API可能会有所不同。随着JDK的发展,jdk.internal.misc.Unsafe可能会引入新的方法或对现有方法进行调整,以适应内部需求
  • 未来发展方向:jdk.internal.misc.Unsafe是JDK团队为了模块化和安全性而进行的改进。未来,更多内部功能可能会转移到jdk.internal命名空间中,而sun.misc.Unsafe则可能逐渐被废弃或减少使用。

参考如下截图,可知sun.misc.Unsafe位于jdk.unsupported包下,不再建议使用:
在这里插入图片描述

在这里插入图片描述
下文的源码分析都是基于jdk.internal.misc.Unsafe

native

查看源码,不难发现不管是public还是private方法,很多方法都标记有native关键字,即所谓的本地方法(Native Method),且这些方法和接口定义很相似,只是定义一个方法签名,没有任何实现,如:

private static native void registerNatives();
static {
    registerNatives();
}

@IntrinsicCandidate
public native int getInt(Object o, long offset);

private native Object staticFieldBase0(Field f);

可将本地方法看作是Java中使用其他编程语言(实际上就是C语言)编写的方法,具体的实现则交给JVM。

实例化

Unsafe提供静态方法getUnsafe用于获取Unsafe实例:

private static final Unsafe theUnsafe = new Unsafe();
public static Unsafe getUnsafe() {
    return theUnsafe;
}

参考方法的注释,开发者需要确保谨慎地安全地使用Unsafe实例。

功能

Unsafe类提供的功能可大致被分为下面7类:

  • 线程调度
  • CAS操作
  • 数据操作
  • 内存操作
  • 内存屏障
  • Class操作
  • 其他

另外,有8种基础数据类型:int、byte、short、char、boolean、long、float、double。

线程调度

说到Unsafe提供的方法,最先想到的可能就是park和unpark,两个方法的签名如下:

@IntrinsicCandidate
public native void park(boolean isAbsolute, long time);
@IntrinsicCandidate
public native void unpark(Object thread);

unpark用于解除对park上阻塞的给定线程的阻塞;如果未阻塞,则导致后续对Park的调用不阻塞。

park方法有两个参数,boolean类型,以及表示纳秒的时间。park方法之所以放在Unsafe类中是因为有unpark在。使用场景:

  • 用于阻塞当前线程,当发生平衡unpark时返回
  • 平衡unpark已经发生
  • 线程被中断
  • isAbsolute为false,且时间不为零,则给定时间纳秒已过去
  • isAbsolute为true,则为给定截止时间自纪元过去以来的毫秒数
  • 其他不明原因

LockSupport里面的park方法就是基于Unsafe.park来实现的:

private static final Unsafe U = Unsafe.getUnsafe();
private static final long PARKBLOCKER = U.objectFieldOffset(Thread.class, "parkBlocker");

public static void park(Object blocker) {
    Thread t = Thread.currentThread();
    setBlocker(t, blocker);
    try {
        if (t.isVirtual()) {
        	// 虚拟线程
            VirtualThreads.park();
        } else {
            U.park(false, 0L);
        }
    } finally {
        setBlocker(t, null);
    }
}

private static void setBlocker(Thread t, Object arg) {
    U.putReferenceOpaque(t, PARKBLOCKER, arg);
}

CAS操作

compareAndSetReference
compareAndExchangeReference
compareAndExchangeReferenceAcquire
compareAndExchangeReferenceRelease

weakCompareAndSetReferencePlain
weakCompareAndSetReferenceAcquire
weakCompareAndSetReferenceRelease
weakCompareAndSetReference

共8个方法,然后对int等8种数据类型,各有8个方法。

数据操作

getAndAddInt:以原子方式将给定值添加到给定对象 o 内给定偏移量处的字段或数组元素的当前值
getAndAddIntRelease
getAndAddIntAcquire
类似地,对除int外的其他7种数据类型,各有3个方法。

getAndSetReference
getAndSetReferenceRelease
getAndSetReferenceAcquire
类似地,对int等8种数据类型,各有3个方法。

位运算主要有三种:或or、与and、异或xor,以boolean+or为例,有如下3个方法:
getAndBitwiseOrBoolean
getAndBitwiseOrBooleanRelease
getAndBitwiseOrBooleanAcquire

针对于运算有三个方法
getAndBitwiseAndBoolean
getAndBitwiseAndBooleanRelease
getAndBitwiseAndBooleanAcquire

针对异或运算也有三个方法
getAndBitwiseXorBoolean
getAndBitwiseXorBooleanRelease
getAndBitwiseXorBooleanAcquire

值得注意的是,这3种位运算可适用于:int、byte、short、char、boolean、long。而对float、double除外。

getReferenceVolatile
putReferenceVolatile
共2个方法,然后对int等8种数据类型,各有2个方法。

getReferenceAcquire
putReferenceRelease
getReferenceOpaque
putReferenceOpaque
共4个方法,对int等8种数据类型,各有3个方法。

内存操作

Unsafe共提供7个方法:

  • allocateMemory:分配新的本地空间
  • reallocateMemory:重新调整内存空间的大小
  • setMemory:有两个重载方法,将内存设置为指定值
  • copyMemory:有两个重载方法,内存拷贝
  • copySwapMemory:有两个重载方法,将所有元素从一个内存块复制到另一块,无条件地动态字节交换元素
  • freeMemory:清除内存
  • writebackMemory:

内存屏障

Unsafe共提供5个方法:

  • loadFence:内存屏障,禁止load操作重排序。屏障前的load操作不能被重排序到屏障后,屏障后的load操作不能被重排序到屏障前
  • storeFence:内存屏障,禁止store操作重排序。屏障前的store操作不能被重排序到屏障后,屏障后的store操作不能被重排序到屏障前
  • fullFence:内存屏障,禁止load、store操作重排序
  • loadLoadFence:
  • storeStoreFence

源码:

@IntrinsicCandidate
public final void loadFence() {
    // If loadFence intrinsic is not available, fall back to full fence.
    fullFence();
}

@IntrinsicCandidate
public final void storeFence() {
    // If storeFence intrinsic is not available, fall back to full fence.
    fullFence();
}

@IntrinsicCandidate
public native void fullFence();

public final void loadLoadFence() {
    loadFence();
}

@IntrinsicCandidate
public final void storeStoreFence() {
    // If storeStoreFence intrinsic is not available, fall back to storeFence.
    storeFence();
}

class操作

Unsafe共提供7个方法:

  • objectFieldOffset:获取静态属性的偏移量
  • staticFieldOffset:获取静态属性的偏移量
  • staticFieldBase:获取静态属性的对象指针
  • arrayBaseOffset:
  • arrayIndexScale:
  • defineClass:让JVM定义一个类而不进行安全检查,类加载器和保护域来自调用者的类。允许程序在运行时动态地创建一个类
  • allocateUninitializedArray:
  • shouldBeInitialized:判断类是否需要初始化(用于获取类的静态属性前进行检测)
  • ensureClassInitialized:
public Class<?> defineClass(String name, byte[] b, int off, int len, ClassLoader loader, ProtectionDomain domain)

从class文件构造实例类,然后利用反射机制获取该类里的方法,然后使用invoke方法执行方法:

private static final Unsafe U = Unsafe.getUnsafe();

private static void testDefineClass() {
    String fileName = "E:\\Student.class";
    File file = new File(fileName);
    try (FileInputStream fis = new FileInputStream(file)) {
        byte[] content = new byte[(int) file.length()];
        fis.read(content);
        Class<?> clazz = U.defineClass(null, content, 0, content.length, null, null);
        Object o = clazz.newInstance();
        Object name = clazz.getMethod("getName").invoke(o, null);
        log.info(age);
    } catch (Exception e) {
        log.error("");
    }
}

其他

其他不好归类的方法:

  • addressSize:返回系统指针的大小。返回值为4(32位系统)或 8(64位系统)
  • pageSize:返回内存页大小,此值永远都为2的幂次方
  • isBigEndian:是否大端字节序
  • getLoadAverage:获取分配给可用处理器的系统运行队列中不同时间段内的平均负载。此方法检索给定的nelem样本并分配给给定的loadavg数组的元素。系统最多施加3个样本,分别代表过去1、5和15分钟的平均值
  • invokeCleaner:基于给定的直接字节缓冲区清理器触发清理动作
  • unalignedAccess:如果平台能够在与所访问的基本类型的类型不对齐的地址处执行访问,则返回true,否则返回false
  • getLongUnaligned:内存对齐操作,适用于:int、short、char
  • putLongUnaligned:内存对齐操作,适用于:int、short、char、
public int getLoadAverage(double[] loadavg, int nelems) {
    if (nelems < 0 || nelems > 3 || nelems > loadavg.length) {
        throw new ArrayIndexOutOfBoundsException();
    }
    return getLoadAverage0(loadavg, nelems);
}

private native int getLoadAverage0(double[] loadavg, int nelems);

Int

以int这个数据类型为对象,有如下方法:

  • getInt
  • putInt
  • compareAndSetInt
  • compareAndExchangeInt
  • compareAndExchangeIntAcquire
  • compareAndExchangeIntRelease
  • weakCompareAndSetIntPlain
  • weakCompareAndSetIntAcquire
  • weakCompareAndSetIntRelease
  • weakCompareAndSetInt
  • getIntVolatile
  • putIntVolatile
  • getIntAcquire
  • putIntRelease
  • getIntOpaque
  • putIntOpaque
  • getAndAddInt
  • getAndAddIntRelease
  • getAndAddIntAcquire
  • getAndSetInt
  • getAndSetIntRelease
  • getAndSetIntAcquire
  • getAndBitwiseOrInt
  • getAndBitwiseOrIntRelease
  • getAndBitwiseOrIntAcquire
  • getAndBitwiseAndInt
  • getAndBitwiseAndIntRelease
  • getAndBitwiseAndIntAcquire
  • getAndBitwiseXorInt
  • getAndBitwiseXorIntRelease
  • getAndBitwiseXorIntAcquire
  • getIntUnaligned
  • putIntUnaligned

Object

还有几个自JDK 12版本被标记为@Deprecated且会移除,用于操作Object的19方法:

  • getObject
  • getObjectVolatile

拓展

Compare-And-Swap和Compare-And-Set

在JVM中,CAS操作主要指Compare-And-Swap,而在Java并发包中,使用的是Compare-And-Set。

Compare-And-Swap是一个底层的原子操作指令,用于实现无锁并发数据结构。CAS是一个硬件层面的原子指令,执行以下步骤:

  • 比较一个内存位置的值是否与给定的预期值相等
  • 如果相等,则将该内存位置的值更新为一个新的值
  • 返回这个内存位置的原始值

CAS的原子性由硬件保证,因此在多线程环境中非常高效,避免使用锁带来的开销和复杂性。

Compare-And-Set
是juc包中的Atomic类使用的方法,与Compare-And-Swap类似,但是一个更高层的抽象,提供给Java开发者使用,通常用于实现乐观锁等无锁算法。

这个方法会:

  • 比较当前值是否等于预期值(expect)
  • 如果相等,则将当前值更新为新的值(update)
  • 返回一个布尔值,指示更新是否成功

区别

  • 层次:Compare-And-Swap是底层的原子操作,通常在硬件指令级别实现。Compare-And-Set是Java中使用的高层抽象,依赖底层的Compare-And-Swap实现
  • 语法和使用:Compare-And-Swap是一种概念(理念),可在多种编程语言和平台上实现。Compare-And-Set是Java特有的,封装在Atomic类中的具体方法
  • 返回值:Compare-And-Swap通常返回内存位置的原始值。Compare-And-Set返回一个布尔值,指示操作是否成功

注解

阅读源码时会发现很多注解,如:

  • @IntrinsicCandidate
  • @ForceInline

TODO,有待后续进一步学习。

虚拟线程

更轻量级的线程,JDK 19版本引入的新特性,在后续版本里持续优化。

TODO,待学习。

参考

标签:Compare,入门,int,Unsafe,misc,内存,讲解,方法
From: https://www.cnblogs.com/johnny-wong/p/18368879

相关文章

  • 黑马毕设分享《基于springboot4S店车辆管理系统》(源码+lw+部署文档+讲解等)
    文章目录1.前言黑马设计——专注大学生的项目实战开发,免费讲解,毕业答疑辅导黑马设计工作室简介:黑马设计是一家专注大学生的项目实战开发,免费讲解,毕业答疑辅导的工作室✅,创始人是硕士毕业于华南理工大学,工科专业,目前团队成员全职+兼职上百余人,运营线上店铺2家,与B站(IT实战,......
  • 黑马毕设分享《基于springboot就业信息管理系统》(源码+lw+部署文档+讲解等)
    文章目录1.前言黑马设计——专注大学生的项目实战开发,免费讲解,毕业答疑辅导黑马设计工作室简介:黑马设计是一家专注大学生的项目实战开发,免费讲解,毕业答疑辅导的工作室✅,创始人是硕士毕业于华南理工大学,工科专业,目前团队成员全职+兼职上百余人,运营线上店铺2家,与B站(IT实战,黑......
  • 黑马毕设分享《基于vue的地方美食分享网站》(源码+lw+部署文档+讲解等)
    文章目录1.前言黑马设计——专注大学生的项目实战开发,免费讲解,毕业答疑辅导黑马设计工作室简介:黑马设计是一家专注大学生的项目实战开发,免费讲解,毕业答疑辅导的工作室✅,创始人是硕士毕业于华南理工大学,工科专业,目前团队成员全职+兼职上百余人,运营线上店铺2家,与B站(IT实战,黑......
  • 【LGR-196-Div.4】洛谷入门赛 #26 题A - H 详细题解--优化思路简洁代码(C++,Python语
    前言:    觉得这个比赛很有意思的,都是暴力题,涉及一些细节,难度比较适合刚学编程语言的,可以很好的锻炼基础还有手速,最后两题也是比较有意思,之后也准备更新atc的比赛题解和洛谷的一些高质量比赛题解(算法网瘾就是想参加各种比赛)   如果觉得有帮助,或者觉得我写的好,......
  • 【数学建模】MATLAB快速入门
    文章目录1.MATLAB界面与基本操作1.1MATLAB的基本操作2.MATLAB字符串和文本2.1string变量2.2char变量3.MATLAB的矩阵运算1.MATLAB界面与基本操作初始界面:刚开始的界面只要一个命令行窗口,为了使编辑界面出现我们需要新建一个文件,注意存放文件的文件夹一定......
  • 【网络安全入门】学习网络安全必须知道的100 个网络基础知识_网络安全知识入门基础_网
    什么是链接?链接是指两个设备之间的连接。它包括用于一个设备能够与另一个设备通信的电缆类型和协议。2OSI参考模型的层次是什么?有7个OSI层:物理层,数据链路层,网络层,传输层,会话层,表示层和应用层。3什么是骨干网?骨干网络是集中的基础设施,旨在将不同的路由和数据......
  • 零基础毫无经验该如何入门学习网络安全?
    首先我们说说什么是网络安全?网络安全是指网络系统的硬件、软件及其系统中的数据受到保护,不因偶然的或者恶意的原因而遭受到破坏、更改、泄露,系统连续可靠正常地运行,网络服务不中断。想学网络安全首先得学习了解网络,学网络的最终目的是为了选择就业方向看清网络拓扑图,对客......
  • 入门mysql数据库
    mysql的入门使用既然我们要使用mysql就要知道为什么要使用mysql持久化把数据保到可掉电式存储设备中以供之后使用,数据持久化意味着将内存中的数据保存到硬盘上加以“固化”,而持久化的实现过程大多通过各种关系数据库来完成。持久化的主要作用是将内存中数据存储在关系型......
  • 一文入门mysql 数据库
    一、数据库概述什么是数据库    数据库是一个用于存储和管理数据的仓库。数据按照特定的格式存储,可以对数据库中的数据进行增加、修改、删除和查询操作。数据库的本质是一个文件系统,按照一定的逻辑结构组织数据,以方便高效地访问和维护。什么是数据库管理系统 ......
  • AUTOSAR&UDS 理论要点及isolar实战-22服务讲解及配置实战(2)
    1.读取数据22服务此部分和22服务讲解及配置实战(1)中保持一致,有需要的小伙伴前往上一博客查看。2.配置实战2.1DcmDsdServiceTables的配置1.DcmDsdSidTabFnc:工具自带的回调函数,调用静态代码包中的服务函数2.DcmDsdSidTabServiceId为0x22,配置22服务;3.DcmDsdSidTabSub......