Java中的Unsafe类详解

Java中的Unsafe类详解

  • 1. Unsafe 概念
  • 2. Unsafe 构造及获取
  • 3. 功能和应用
    • 3.1 内存管理
      • 3.1.1 普通读写
      • 3.1.2 volatile 读写
      • 3.1.3 有序读写
      • 3.1.4 直接操作内存
    • 3.2 CAS
    • 3.3 偏移量
    • 3.4 线程调度
    • 3.5 类加载
    • 3.6 内存屏障
    • 3.7 其他操作
  • 4. 潜在风险和挑战
  • 5. 最佳实践
    • 5.1 使用案例:CAS 操作
  • 6. 结论

在这里插入图片描述

1. Unsafe 概念

Java 作为一门广泛应用于企业级应用开发的编程语言,为了保障程序的稳定性和安全性,通常限制了开发者对底层内存和硬件的直接访问。然而,Java 中的 Unsafe 类却为开发者提供了一种突破这些限制的方式,让他们可以直接操作内存、线程和对象,同时也引发了一系列潜在的风险和挑战。

2. Unsafe 构造及获取

Unsafe 类使用 final 修饰,不允许继承,且构造函数是 private,使用了饿汉式单例,通过一个静态方法 getUnsafe() 来获取实例。
先看一下源码:

public final class Unsafe {private static final Unsafe theUnsafe;private static native void registerNatives();private Unsafe() {}@CallerSensitivepublic static Unsafe getUnsafe() {Class var0 = Reflection.getCallerClass();if (!VM.isSystemDomainLoader(var0.getClassLoader())) {throw new SecurityException("Unsafe");} else {return theUnsafe;}}
}

在 getUnsafe 方法中对单例模式中的对象获取做了限制,如果是普通的调用会抛出一个 SecurityException 异常。只有由主类加载器加载的类才能调用这个方法。

获取 Unsafe 对象的方式

  1. 通过 Unsafe.getUnsafe()
    Unsafe unsafe = Unsafe.getUnsafe();
    
  2. 通过反射来获取
    Class<Unsafe> unsafeClass = Unsafe.class;
    Field theUnsafe = unsafeClass.getDeclaredField("theUnsafe");
    theUnsafe.setAccessible(true);
    Object o = theUnsafe.get(null);
    Unsafe unsafe1 = (Unsafe) o;
    

3. 功能和应用

Unsafe 类提供了一些能够绕过 Java 语言安全机制的方法,例如直接操作内存、CAS(比较并交换)操作、分配和释放内存等。这使得开发者可以在某些情况下获得更高的性能,但同时也需要承担更大的风险和责任。

在这里插入图片描述

一些用途包括:

  • 手动管理内存:开发者可以使用 Unsafe 类手动分配和释放内存,从而实现更精细的内存管理。
  • 原子操作:Unsafe 提供了原子操作方法,使开发者可以实现高效的多线程并发控制。
  • 绕过安全检查:Unsafe 可以绕过一些 Java 语言层面的安全检查,但这也会导致潜在的安全漏洞。

3.1 内存管理

Unsafe 的内存管理功能主要包括:普通读写、volatile读写、有序读写、直接操作内存等分配内存与释放内存的功能。

3.1.1 普通读写

Unsafe 可以读写一个类的属性,即便这个属性是私有的,也可以对这个属性进行读写。

public native int getInt(Object var1, long var2);public native void putInt(Object var1, long var2, int var4);

getInt 等于从对象的指定偏移地址处读取一个值。putInt等于在对象指定偏移处写入一个值。其他原始类型也提供有对应的方法。此外,Unsafe 的 getByte、putByte 方法提供了直接在一个地址上就行读写的功能。

3.1.2 volatile 读写

普通的读写无法保证可见性和有序性,而 volatile 读写就可以保证;但是相对普通读写更加昂贵。

public native int getIntVolatile(Object var1, long var2);public native void putIntVolatile(Object var1, long var2, int var4);

3.1.3 有序读写

有序写入只保证写入的有序性,不保证可见性,就是说一个线程的写入不保证其他线程立马可见。
而与 volatile 写入相比 putOrderedXX 吸入代价相对较低,putOrderedXX 写入不保证可见性,但是保证有序性,所谓有序性,就是保证指令不会重排序。

3.1.4 直接操作内存

Unsafe 提供了直接操作内存的能力:

public native long allocateMemory(long var1);public native long reallocateMemory(long var1, long var3);public native void setMemory(Object var1, long var2, long var4, byte var6);public void setMemory(long var1, long var3, byte var5) {this.setMemory((Object)null, var1, var3, var5);
}public native void copyMemory(Object var1, long var2, Object var4, long var5, long var7);public native void freeMemory(long var1);

也提供了一些获取内存信息的方法:getAddress、addressSize、pageSize
注意:利用 copyMemory 方法可以实现一个通用的对象拷贝方法,无需再对每一个对象都实现 clone 方法,但只能做到对象浅拷贝。

3.2 CAS

Unsafe 类的 CAS 操作作为 Java 的锁机制提供了一种新的解决方法,比如 AtomicInteger 等类都是通过该方法来实现的。compareAndSwap* 方法是原子的,可以避免的繁琐的锁机制,提高代码效率。

public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);

CAS 一般用于乐观锁,它在 Java 中有广泛的应用,ReentrantLock、ConcurrentHashMap、ConcurrentLinkedQueue 等都有用到CAS来实现乐观锁。

3.3 偏移量

Unsafe 提供以下方法获取对象的指针,通过对指针进行偏移,不仅可以直接修改指针指向的数据(及时它们是私有的),甚至可以找到JVM已经认定为垃圾、可以进行回收的对象。

// 获取静态属性Field在对象中的偏移量,读写静态属性时必须获取其偏移量
public native long staticFieldOffset(Field var1);// 获取费静态属性Field在对象实例中的偏移量,读写对象的费静态属性时会用到这个偏移量
public native long objectFieldOffset(Field var1);// 返回Field所在的对象
public native Object staticFieldBase(Field var1);// 返回数组中第一个元素实际地址相对整个数组对象的地址的偏移量
public native int arrayBaseOffset(Class<?> var1);// 计算数组中第一个元素所占用的内存空间
public native int arrayIndexScale(Class<?> var1);

3.4 线程调度

// 唤醒线程    
public native void unpark(Object var1);// 挂起线程
public native void park(boolean var1, long var2);

通过 park 方法将线程挂起,线程将一直阻塞到超时或者中断条件出现。unpark 方法可以终止一个挂起的线程,使其恢复正常。
整个并发框架中对线程的挂起操作被封装在 LockSupport 类中,LockSupport 类中有各种版本 park 方法,但最终都调用了 Unsafe.park() 方法。

3.5 类加载

// 方法定义一个类,用于动态地创建类
public native Class<?> defineClass(String var1, byte[] var2, int var3, int var4, ClassLoader var5, ProtectionDomain var6);// 动态的创建一个匿名内部类
public native Class<?> defineAnonymousClass(Class<?> var1, byte[] var2, Object[] var3);// 判断是否需要初始化一个类
public native boolean shouldBeInitialized(Class<?> var1);// 保证已经初始化过一个类
public native void ensureClassInitialized(Class<?> var1);

3.6 内存屏障

// 保证在这个屏障之前的所有读操作都已完成
public native void loadFence();// 保证在这个屏障之前的所有写操作都已完成
public native void storeFence();// 保证在这个屏障之前的所有读写操作都已完成
public native void fullFence();

3.7 其他操作

当然,Unsafe 类中还提供了大量其他的方法,比如上面提供的CAS操作,以 AtomicInteger 为例,当我们调用 getAndIncrement、getAndDecrement 等方法时,本质上调用就是 Unsafe 类的 getAndAddInt 方法。

// 返回系统指针的大小。返回值为4(32位系统)或8(64位系统)。
public native int addressSize();// 内存页的大小,此值为2的幂次方
public native int pageSize();

4. 潜在风险和挑战

尽管 Unsafe 类在某些情况下可能提供了极大的灵活性和性能优势,但它也带来了一些严重的潜在问题:

  • 内存泄漏:手动管理内存可能导致难以察觉的内存泄漏,从而降低应用的稳定性和性能。
  • 安全漏洞:绕过安全检查可能导致潜在的安全漏洞,使应用容易受到恶意攻击。
  • 不稳定性:直接操作内存和线程可能导致应用的不稳定性和不可预测的行为。

5. 最佳实践

虽然 Unsafe 类提供了一些强大的功能,但在大多数情况下,开发者应该避免直接使用它。如果确实需要使用 Unsafe,请遵循以下最佳实践:

  • 仅在必要时使用:只有在必须绕过 Java 安全机制并获得更高性能时才考虑使用 Unsafe。
  • 小心操作:确保在使用 Unsafe 时仔细考虑可能的风险,并采取适当的措施来减少潜在问题。
  • 文档和测试:详细记录 Unsafe 使用情况,并进行充分的测试,以确保应用在各种情况下都能够稳定运行。

5.1 使用案例:CAS 操作

下面是一个使用 Unsafe 进行 CAS(比较并交换)操作的简单案例。CAS 是一种常见的并发控制手段,可用于线程安全的更新变量。

import sun.misc.Unsafe;public class CasExample {private static final Unsafe unsafe = Unsafe.getUnsafe();private static final long valueOffset;static {try {valueOffset = unsafe.objectFieldOffset(CasExample.class.getDeclaredField("value"));} catch (NoSuchFieldException e) {throw new Error(e);}}private volatile int value = 0;public void increment() {int current;do {current = unsafe.getIntVolatile(this, valueOffset);} while (!unsafe.compareAndSwapInt(this, valueOffset, current, current + 1));}public int getValue() {return value;}
}

6. 结论

Java 中的 Unsafe 类为开发者提供了一种强大而危险的工具,可以用于在某些特定情况下实现高性能的操作。然而,使用 Unsafe 也需要开发者对风险有清晰的认识,并采取适当的措施来确保应用的稳定性和安全性。

请注意,在使用 Unsafe 时需要格外小心,遵循最佳实践,以免引发不必要的问题和风险。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/79787.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

QtAV for ubuntu16.04

下载ubuntu https://releases.ubuntu.com/16.04/ubuntu-16.04.7-desktop-amd64.iso 下载ffmpeg https://ffmpeg.org/download.html 下载QtAV https://github.com/wang-bin/QtAV/releases 更新 sudo apt update 安装库 sudo apt-get install libglu1-mesa-dev freeglut3-dev…

【算法系列 | 7】深入解析查找算法之—布隆过滤器

序言 心若有阳光&#xff0c;你便会看见这个世界有那么多美好值得期待和向往。 决定开一个算法专栏&#xff0c;希望能帮助大家很好的了解算法。主要深入解析每个算法&#xff0c;从概念到示例。 我们一起努力&#xff0c;成为更好的自己&#xff01; 今天第3讲&#xff0c;讲一…

【数据结构】链表(一)

链表&#xff08;一&#xff09; 文章目录 链表&#xff08;一&#xff09;01 引入02 概念及结构03 单向不带头不循环链表实现3.1 创建节点类型3.2 简易创建一个链表3.3 遍历链表每个节点3.4 获取链表长度3.5 查找是否包含关键字key是否在单链表当中3.6 头插法3.7 尾插法3.8 任…

MySQL 重置root 密码

5.7 版本 首先要把服务mysql57 关闭 net stop MySQL57 在安装的mysql57的程序的bin中 运行cmd&#xff08;管理员运行&#xff09; mysqld --defaults-file‘mysql存放数据的位置\my.ini’ --skip-grant-tables 上图 错误 注意&#xff1a;如果遇到mysqld: Can’t change dir…

【从零学习python 】02. 开发工具介绍

文章目录 编写Python代码一、常见的代码编辑工具二、运行Python程序三、Pycharm的下载和安装PyCharm的主要功能区域进阶案例 编写Python代码 根据我们之前介绍的知识&#xff0c;我们知道&#xff0c;所谓代码其实就是将一段普通文本按照一定的规范编写&#xff0c;然后交给电…

Cesium 加载ArcGIS Server切片服务错级问题

1.首先上官方api说明 ArcGisMapServerImageryProvider - Cesium Documentation 里面没有 zoomoffset参数!!! 2.如果按照互联网栅格切片规则 3857、4326、4490常用切片层级参数,则直接加载显示地图 viewer.imageryLayers.addImageryProvider(new Cesium.ArcGisMapServerI…

【Spring Boot】(三)深入理解 Spring Boot 日志

文章目录 前言一、日志文件的作用二、Spring Boot 中的日志2.1 查看输出的日志信息2.2 日志格式二、Spring Boot 中的日志2.1 查看输出的日志信息2.2 日志格式 三、自定义日志输出3.1 日志框架3.2 日志对象的获取3.3 使用日志对象打印日志 四、日志级别4.1 日志级别的作用4.2 日…

Oracle-expdp报错ORA-39077、06502(Bug-16928674)

问题: 用户在使用expdp进程导出时&#xff0c;出现队列报错ORA-39077、ORA-06502 ORA-31626: job does not exist ORA-31638: cannot attach to job SYS_EXPORT_SCHEMA_01 for user SYS ORA-06512: at "SYS.DBMS_SYS_ERROR", line 95 ORA-06512: at "SYS.KUPV$…

【修正-高斯拉普拉斯滤波器-用于平滑和去噪】基于修正高斯滤波拉普拉斯地震到达时间自动检测研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

解密HTTP代理爬虫中的IP代理选择与管理策略

在当今数据驱动的世界中&#xff0c;HTTP代理爬虫作为一项重要的数据采集工具&#xff0c;其成功与否往往取决于IP代理的选择与管理策略。作为一家专业的HTTP代理产品供应商&#xff0c;我们深知IP代理在数据采集中的重要性。在本文中&#xff0c;我们将分享一些关于HTTP代理爬…

图像膨胀+滤波达到边缘外扩模糊效果

有一个扯淡需求, 根据某些格网值渲染对应的颜色, 我们做的实现方案是按照色代码渐变做颜色映射, 但是某些厂家不顾结果正确性与否, 应是为了好看做的好看, 将边界膨胀模糊, 一个非风场,力场类似场数据做了一个类似场的渲染效果, 也不知道说啥好, 例如原始图渲染如下 经过一系列…

Spring Boot配置文件与日志文件

1. Spring Boot 配置文件 我们知道, 当我们创建一个Spring Boot项目之后, 就已经有了配置文件存在于目录结构中. 1. 配置文件作用 整个项目中所有重要的数据都是在配置文件中配置的&#xff0c;比如: 数据库的连接信息 (包含用户名和密码的设置) ;项目的启动端口;第三方系统的调…

资深测试总结,Web自动化测试POM设计模式封装框架,看这篇就够了...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 线性脚本 import…

ParallelCollectionRDD [0] isEmpty at KyuubiSparkUtil.scala:48问题解决

ParallelCollectionRDD [0] isEmpty at KyuubiSparkUtil.scala:48问题解决 这个问题出现在使用Kyubi Spark Util处理ParallelCollectionRDD的过程中&#xff0c;具体是在KyubiSparkUtil.scala文件的第48行调用isEmpty方法时出现的。该问题可能是由以下几个原因引起的&#xff1…

FFmpeg将编码后数据保存成mp4

以下测试代码实现的功能是&#xff1a;持续从内存块中获取原始数据&#xff0c;然后依次进行解码、编码、最后保存成mp4视频文件。 可保存成单个视频文件&#xff0c;也可指定每个视频文件的总帧数&#xff0c;保存多个视频文件。 为了便于查看和修改&#xff0c;这里将可独立的…

[CKA]考试之检查可用节点数量

由于最新的CKA考试改版&#xff0c;不允许存储书签&#xff0c;本博客致力怎么一步步从官网把答案找到&#xff0c;如何修改把题做对&#xff0c;下面开始我们的 CKA之旅 题目为&#xff1a; Task 检查集群中有多少节点为Ready状态&#xff08;不包括被打上 Taint&#xff1…

FPGA学习——Altera IP核调用之PLL篇

文章目录 一、IP核1.1 IP核简介1.2 FPGA中IP核的分类1.3 IP核的缺陷 二、PLL简介2.1 什么是PLL2.2 PLL结构图2.3 C4开发板上PLL的位置 三、IP核调用步骤四、编写测试代码五、总结 一、IP核 1.1 IP核简介 IP核&#xff08;知识产权核&#xff09;&#xff0c;是在集成电路的可…

视频监控汇聚平台EasyCVR视频分享页面WebRTC流地址播放不了是什么原因?

开源EasyDarwin视频监控TSINGSEE青犀视频平台EasyCVR能在复杂的网络环境中&#xff0c;将分散的各类视频资源进行统一汇聚、整合、集中管理&#xff0c;在视频监控播放上&#xff0c;TSINGSEE青犀视频安防监控汇聚平台可支持1、4、9、16个画面窗口播放&#xff0c;可同时播放多…

机器学习笔记之优化算法(八)简单认识Wolfe Condition的收敛性证明

机器学习笔记之优化算法——简单认识Wolfe Condition收敛性证明 引言回顾&#xff1a; Wolfe \text{Wolfe} Wolfe准则准备工作推导条件介绍推导结论介绍 关于 Wolfe \text{Wolfe} Wolfe准则收敛性证明的推导过程 引言 上一节介绍了非精确搜索方法—— Wolfe \text{Wolfe} Wolf…

【Python】从同步到异步多核:测试桩性能优化,加速应用的开发和验证

目录 测试工作中常用到的测试桩mock能力 应用场景 简单测试桩 http.server扩展&#xff1a;一行命令实现一个静态文件服务器 性能优化&#xff1a;使用异步响应 异步响应 能优化&#xff1a;利用多核 gunicorn 安装 gunicorn 使用 gunicorn 启动服务 性能优化&#…