Unsafe

news2025/1/21 17:36:05

1. 概念介绍

sun.misc.Unsafe 是 Java 中的一个特殊类,它提供了一组低级别的、不安全的操作,这些操作通常是 JVM 内部使用的。由于这些操作非常强大且危险,因此 Unsafe 类被设计为只能在受信任的代码中使用。

2. 主要功能和用途

  1. 内存操作

    • 直接内存访问Unsafe 允许直接操作内存地址,绕过 Java 的内存管理机制。例如,allocateMemoryfreeMemory 方法可以分配和释放内存。
    • 数组操作arrayBaseOffsetarrayIndexScale 方法可以获取数组的基地址和元素大小,从而实现对数组的直接操作。
  2. 对象操作

    • 对象创建allocateInstance 方法可以直接创建对象实例,而不调用构造函数。
    • 字段访问objectFieldOffset 方法可以获取对象字段的偏移量,从而实现对字段的直接读写。
  3. 线程操作

    • 线程挂起和恢复parkunpark 方法可以挂起和恢复线程。
  4. CAS(Compare-And-Swap)操作

    • 原子操作compareAndSwapIntcompareAndSwapLong 等方法提供了原子性的比较和交换操作,这是实现无锁数据结构的基础。
  5. 内存屏障

    • 内存屏障loadFencestoreFencefullFence 方法可以插入内存屏障,控制内存操作的顺序。

示例代码

以下是一些 Unsafe 类的常见用法示例:

import sun.misc.Unsafe;
import java.lang.reflect.Field;

public class UnsafeExample {
    public static void main(String[] args) throws Exception {
        // 获取 Unsafe 实例
        Unsafe unsafe = getUnsafe();

        // 直接分配内存
        long address = unsafe.allocateMemory(1024);
        System.out.println("Allocated memory at address: " + address);

        // 写入和读取内存
        unsafe.putByte(address, (byte) 42);
        byte value = unsafe.getByte(address);
        System.out.println("Read byte from memory: " + value);

        // 释放内存
        unsafe.freeMemory(address);

        // 创建对象实例而不调用构造函数
        ExampleClass obj = (ExampleClass) unsafe.allocateInstance(ExampleClass.class);
        System.out.println("Created instance without calling constructor: " + obj);

        // CAS 操作
        long offset = unsafe.objectFieldOffset(ExampleClass.class.getDeclaredField("value"));
        boolean success = unsafe.compareAndSwapInt(obj, offset, 0, 42);
        System.out.println("CAS operation result: " + success);
        System.out.println("Updated value: " + obj.getValue());
    }

    private static Unsafe getUnsafe() throws Exception {
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        return (Unsafe) field.get(null);
    }

    static class ExampleClass {
        private int value;

        public ExampleClass() {
            this.value = 10;
        }

        public int getValue() {
            return value;
        }
    }
}

其中运行结果如下:
在这里插入图片描述

	private static Unsafe getUnsafe() throws Exception {
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        return (Unsafe) field.get(null);
    }

其中theUnsafe是unsafe中的属性,目的是为了获取unsage对象来操作。

源码如下

package sun.misc;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.security.ProtectionDomain;
import sun.reflect.CallerSensitive;
import sun.reflect.Reflection;

public final class Unsafe {
    private static final Unsafe theUnsafe;
    public static final int INVALID_FIELD_OFFSET = -1;
    public static final int ARRAY_BOOLEAN_BASE_OFFSET;
    ```

3. 注意事项

  1. 安全性Unsafe 类提供了非常底层的操作,使用不当可能会导致 JVM 崩溃、内存泄漏或其他未定义行为。因此,它通常只应在受信任的代码中使用。
  2. 兼容性Unsafe 类是 sun.misc 包的一部分,这意味着它不是 Java 标准库的一部分,可能在未来的 Java 版本中被移除或更改。
  3. 性能:虽然 Unsafe 类提供了高性能的操作,但它们通常也更复杂且难以调试。

总结

sun.misc.Unsafe 类提供了许多强大的底层操作,适用于需要高性能或特殊操作的场景。然而,由于其潜在的危险性和非标准性,使用时应格外小心。

4. 举例说明: AtomicBoolean

4.1 源码如下
public class AtomicBoolean implements java.io.Serializable {
    private static final long serialVersionUID = 4654671469794556979L;
    // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicBoolean.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile int value;
    ...
    public final boolean compareAndSet(boolean expect, boolean update) {
        int e = expect ? 1 : 0;
        int u = update ? 1 : 0;
        return unsafe.compareAndSwapInt(this, valueOffset, e, u);
    }
    /**
     * Atomically sets the value to the given updated value
     * if the current value {@code ==} the expected value.
     *
     * @param expect the expected value
     * @param update the new value
     * @return {@code true} if successful. False return indicates that
     * the actual value was not equal to the expected value.
     */
    public boolean weakCompareAndSet(boolean expect, boolean update {
        int e = expect ? 1 : 0;
        int u = update ? 1 : 0;
        return unsafe.compareAndSwapInt(this, valueOffset, e, u);
    }
    
    /**
     * Unconditionally sets to the given value.
     *
     * @param newValue the new value
     */
    public final void set(boolean newValue) {
        value = newValue ? 1 : 0;
    }
    
    /**
     * Eventually sets to the given value.
     *
     * @param newValue the new value
     * @since 1.6
     */
    public final void lazySet(boolean newValue) {
        int v = newValue ? 1 : 0;
        unsafe.putOrderedInt(this, valueOffset, v);
    }

    /**
     * Atomically sets to the given value and returns the previous value.
     *
     * @param newValue the new value
     * @return the previous value
     */
    public final boolean getAndSet(boolean newValue) {
        boolean prev;
        do {
            prev = get();
        } while (!compareAndSet(prev, newValue));
        return prev;
    }
    

方法中getAndSet 是原子操作,来设置值。
说实话weakCompareAndSetcompareAndSet两个方法没看出来有什么区别。
为了保证原子性,value还是使用volatile 修饰。

volatile 是 Java 中的一个关键字,用于修饰变量。它的主要作用是确保变量的可见性和禁止指令重排序。以下是 volatile 的详细作用和使用场景:

  1. 可见性(Visibility)
    当一个变量被声明为 volatile 时,所有线程都能看到该变量的最新值。具体来说:
  • 写操作:当一个线程对 volatile 变量进行写操作时,该操作会立即刷新到主内存中。
  • 读操作:当一个线程对 volatile 变量进行读操作时,该操作会从主内存中读取最新的值,而不是从线程的本地缓存(如 CPU 缓存)中读取。
  1. 禁止指令重排序(Preventing Instruction Reordering)
    volatile 变量的读写操作会被编译器和处理器特殊对待,以确保它们不会被重排序。具体来说:
  • 写操作:在写 volatile 变量之前,所有之前的写操作都会被刷新到主内存中。
  • 读操作:在读 volatile 变量之后,所有之后的读操作都会从主内存中读取最新的值。
4.2 使用场景

volatile 通常用于以下场景:

  1. 状态标志:
    当一个变量用于表示某种状态标志(如 boolean 类型的标志)时,可以使用 volatile 来确保所有线程都能看到最新的状态。
    例如,一个线程可以设置一个 volatile 的 boolean 变量来通知其他线程某个操作已经完成。
  2. 单例模式的双重检查锁定(Double-Checked Locking):
    在单例模式的实现中,可以使用 volatile 来确保实例的初始化操作不会被重排序,从而避免潜在的线程安全问题。
4.3 注意事项
  • 原子性
    • volatile 只能保证变量的可见性和禁止指令重排序,但不能保证复合操作的原子性。例如,volatile 不能替代 synchronized 或 AtomicInteger 等原子类来实现原子操作。
  • 性能:
    • volatile 的性能开销比普通变量要高,因为它涉及到主内存的读写操作。因此,只有在确实需要确保可见性和禁止重排序时才使用 volatile。

5. compareAndSwapInt 底层原理

Unsafe 类的 compareAndSwapInt 方法是一个本地方法(native method),它的底层实现依赖于操作系统和硬件平台。具体来说,它通常利用了处理器提供的原子性指令(如 x86 架构上的 CMPXCHG 指令)来实现比较和交换操作。

5.1 底层实现原理
  1. 处理器指令

    • compareAndSwapInt 方法的核心是利用处理器的原子性指令来确保操作的原子性。在 x86 架构上,这个指令通常是 CMPXCHG
    • CMPXCHG 指令会比较目标内存位置的值与一个期望值,如果相等,则将新值写入目标内存位置;否则,不进行任何操作。
  2. 内存屏障

    • 为了确保操作的原子性和顺序性,compareAndSwapInt 方法通常会插入内存屏障(memory barrier)。内存屏障可以防止指令重排序,确保在屏障之前的所有内存操作都完成之后,屏障之后的内存操作才能开始。
  3. 操作系统支持

    • 不同的操作系统和硬件平台可能有不同的实现方式。例如,在 Linux 上,JVM 可能会调用内核提供的原子操作函数(如 __sync_bool_compare_and_swap)。
5.2 示例代码

以下是一个简化的伪代码,展示了 compareAndSwapInt 方法的底层实现思路:

// 伪代码,实际实现可能更复杂
bool compareAndSwapInt(Object* obj, long offset, int expected, int newValue) {
    // 获取对象的内存地址
    int* address = (int*)((char*)obj + offset);

    // 使用处理器指令进行比较和交换
    bool success = false;
    __asm__ volatile (
        "lock cmpxchg %4, (%3)\n"
        "sete %0\n"
        : "=q" (success)
        : "a" (expected), "q" (newValue), "r" (address)
        : "memory"
    );

    return success;
}
5.3 解释
  1. __asm__ volatile
    • 这是 GCC 内联汇编的语法,用于在 C 代码中嵌入汇编指令。
    • lock cmpxchg %4, (%3):这是 x86 架构上的 CMPXCHG 指令,它会锁定总线,确保操作的原子性。
    • sete %0:如果 CMPXCHG 操作成功(即目标内存位置的值等于期望值),则将 success 设置为 true
  2. 内存屏障
    • __asm__ volatile 中的 "memory" 约束表示这是一个内存屏障,确保在执行这段汇编代码时,之前的所有内存操作都已完成。
5.4 总结

compareAndSwapInt 方法的底层实现依赖于处理器提供的原子性指令(如 CMPXCHG)和内存屏障,以确保操作的原子性和顺序性。不同的操作系统和硬件平台可能有不同的实现方式,但核心思想是利用处理器的原子性指令来实现比较和交换操作。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32-CAN总线

1.CAN总线简介 CAN总线是由BOSCH公司开发的一种简洁易用、传输速度快、易扩展、可靠性高的串行通信总线 2.CAN总线特征 两根通信线(CAN_H、CAN_L),线路少,无需共地差分信号通信(相对的是单端信号)&#…

Linux初识:【版本控制器Git】【调试器gdb/cgdb使用】

目录 一.版本控制器Git 1.1版本控制器 1.2Git的操作 1.2.1从远端仓库到本地 1.2.2工作区到本地暂存区 1.2.3本地暂存区到本地仓库 1.2.4本地仓库到远程仓库 1.2.5 .gitignore 1.2.6Windows上操作(需要安装Tortoisegit) 1.2.7同步远端和当地 二调…

【MATLAB源码-第259期】基于matlab的64QAM调制解调锁相环环载波同步仿真,对比前后星座图,输出锁相环响应曲线。

操作环境: MATLAB 2022a 1、算法描述 1. 概述 在现代数字通信系统中,为了提高频谱利用率和数据传输效率,经常采用多阶调制技术。64QAM(64阶正交幅度调制)便是其中的一种,它通过将数据映射到64个不同的复…

BEVFusion论文阅读

1. 简介 融合激光雷达和相机的信息已经变成了3D目标检测的一个标准,当前的方法依赖于激光雷达传感器的点云作为查询,以利用图像空间的特征。然而,人们发现,这种基本假设使得当前的融合框架无法在发生 LiDAR 故障时做出任何预测&a…

大模型LLM-微调 RAG

RAG小结 这篇文章是一篇关于大型语言模型(LLMs)增强技术的综述论文,特别聚焦于检索增强生成(Retrieval-Augmented Generation, RAG)这一领域。详细考察了RAG的发展、技术基础、关键技术、评估框架以及未来的研究方向。…

TongESB7.1.0.0如何使用dockercompose运行镜像(by lqw)

文章目录 安装准备安装 安装准备 1.安装好docker和dockercompose: docker、docker-compose安装教程,很详细 2.上传好安装相关文件 安装 使用以下命令导入管理端镜像和运行时镜像 docker load -i tongesb_manage_7100.tar docker load -i tongesb_se…

Acwing-基础算法课笔记之基础算法(二分)

Acwing-基础算法课笔记之基础算法(二分) 一、二分查找的概念1、使用二分的条件2、二分查找的算法流程 二、左闭右闭写法[left,right]三、左闭右开写法[left,right)四、浮点数的二分 一、二分查找的概念 1、使用二分的条件 1、必须是数组(顺…

PHP教育系统小程序

🌐 教育系统:全方位学习新体验,引领未来教育风尚 🚀 教育系统:创新平台,智慧启航 📱 教育系统,一款深度融合科技与教育的创新平台,匠心独运地采用先进的ThinkPHP框架与U…

蓝桥杯R格式--高精度算法模拟

#include <bits/stdc.h> using namespace std; int pos,p1; int ib[1566]; int an[1567]; int n; string a,b; int main() {cin>>n>>a;for(int ia.size()-1; i>0; i--){if(a[i]!.){pos;b.insert(b.end(),a[i]);///string 插char用insert/push_back} …

【电视盒子】HI3798MV300刷机教程笔记/备份遥控码修复遥控器/ADB/线刷卡刷/电视盒子安装第三方应用软件

心血来潮&#xff0c;看到电视机顶盒满天飞的广告&#xff0c;想改造一下家里的电视盒子&#xff0c;学一下网上的人刷机&#xff0c;但是一切都不知道怎么开始&#xff0c;虽然折腾了一天&#xff0c;以失败告终&#xff0c;还是做点刷机笔记。 0.我的机器 年少不会甄别&…

实战经验:使用 Python 的 PyPDF 进行 PDF 操作

文章目录 1. 为什么选择 PyPDF&#xff1f;2. 安装 PyPDF3. PDF 文件的合并与拆分3.1 合并 PDF 文件3.2 拆分 PDF 文件 4. 提取 PDF 文本5. 修改 PDF 元信息6. PDF 加密与解密6.1 加密 PDF6.2 解密 PDF 7. 页面旋转与裁剪7.1 旋转页面7.2 裁剪页面 8. 实战经验总结 PDF 是一种非…

C++11的多线程

目录 引言 thread类的简单介绍 接口解读 使用范例 move的作用--将资源“夺舍” 原子性操作库(atomic) lock_guard与unique_lock 前置知识&#xff1a;mutex锁&#xff08;类似linux下的ptrhead_mutex_t数据&#xff09; mutex的种类 1. std::mutex 2. std::recursive_…

电子应用设计方案96:智能AI充电器系统设计

智能 AI 充电器系统设计 一、引言 智能 AI 充电器系统旨在为各种电子设备提供高效、安全、智能的充电解决方案&#xff0c;通过融合人工智能技术&#xff0c;实现自适应充电、优化充电效率和保护电池寿命。 二、系统概述 1. 系统目标 - 自适应识别不同设备的充电需求&#xf…

logback日志自定义占位符

前言 在大型系统运维中&#xff0c;很大程度上是需要依赖日志的。在java大型web工程中&#xff0c;一般都会使用slf4jlogback这一个组合来实现日志的管理。 logback中很多现成的占位符可以可以直接使用&#xff0c;比如线程号【%t】、时间【%d】、日志等级【%p】&#xff0c;…

Stable Diffusion 3.5 模型在 Linux 上的部署指南

文章目录 前言-参考资料如下一. ComfyUI安装二.模型下载2.1 安装GGUF和T5 xxl编码模型2.2 安装ComfyUI辅助插件2.3 启动ComfyUI2.4 基础ComfyUI和SD3.5配置2.5 demo 前言-参考资料如下 ComfyUI WIKI教程 sd3.5 github 尝试过sd集成ollama&#xff0c;但是sd在ollama上无法良好…

【Go】Go数据类型详解—指针

1. 前言 在我看来&#xff0c;一门编程语言语法的核心就在于数据类型。而各类编程语言的基本数据类型大致相同&#xff1a;int整型、float浮点型、string字符串类型、bool布尔类型&#xff0c;但是在一些进阶数据类型上就有所不同了。本文将会介绍Go语言当中核心的数据类型——…

Grafana 统一可视化了,告警如何统一?

对于大部分公司&#xff0c;通常都不止一套监控、可观测性相关的系统&#xff0c;云上的、云下的&#xff0c;开源的、商业的&#xff0c;指标的、日志的、链路的&#xff0c;各个系统体验不同&#xff0c;权限难管&#xff0c;如何统一化并为各个团队赋能&#xff0c;是很多技…

LeetCode 110.平衡二叉树

题目描述 给定一个二叉树&#xff0c;判断它是否是平衡二叉树。 示例 1&#xff1a; 示例 2&#xff1a; 输入&#xff1a;root [1,2,2,3,3,null,null,4,4] 输出&#xff1a;false 示例 3&#xff1a; 输入&#xff1a;root [] 输出&#xff1a;true 提示&#xff1a; …

【银河麒麟高级服务器操作系统】业务访问慢网卡丢包现象分析及处理过程

了解更多银河麒麟操作系统全新产品&#xff0c;请点击访问 麒麟软件产品专区&#xff1a;product.kylinos.cn 开发者专区&#xff1a;developer.kylinos.cn 文档中心&#xff1a;document.kylinos.cn 交流论坛&#xff1a;forum.kylinos.cn 服务器环境以及配置 【内核版本…

软件测试—— 接口测试(HTTP和HTTPS)

软件测试—— 接口测试&#xff08;HTTP和HTTPS&#xff09; HTTP请求方法GET特点使用场景URL结构URL组成部分URL编码总结 POST特点使用场景请求结构示例 请求标头和响应标头请求标头&#xff08;Request Headers&#xff09;示例请求标头 响应标头&#xff08;Response Header…