java Unsafe详细解析

Java Unsafe详细解析

简介

Java Unsafe 是 JDK 提供的一个支持直接操作内存、线程、JVM 的类库。由于 Unsafe 操作的是内存,所以它可以绕过 JVM 的安全检查,说白了就是越过了 Java 的限制,直接操作底层内存。不是直接通过 new 实例化对象进行使用,而是通过反射或本地方法调用获取一个实例。

使用

Unsafe 类主要包含以下方法:

  1. 内存存取操作

    • putXXX():存放基本类型到指定的内存地址;
    • getXXX():从指定地址获取指定类型的值。
  2. 对象创建和销毁

    • allocateInstance(Class cls):实例化一个没有调用构造方法的对象;
    • freeMemory(obj):释放对象空间。
  3. 数组操作

    • arrayBaseOffset(Class<?> arrayClass):获取指定类型数组第一个元素的偏移量;
    • arrayIndexScale(Class<?> arrayClass):获取指定类型数组每个元素占用的空间大小;
    • getObject(Object array, long index):获取指定索引位置的元素;
    • putObject(Object array, long index, Object value):设置指定索引位置的元素值。
  4. 线程和锁操作

    • park(boolean isAbsolute, long time):暂停线程运行指定时间,单位为纳秒;
    • unpark(Object thread):继续运行指定的线程;
    • monitorEnter(Object obj):获取对象锁;
    • monitorExit(Object obj):释放对象锁。

示例1:修改String对象值

public class ChangeStringValue {
    public static void main(String[] args) throws Exception {
        String str = "Hello, World!";
        System.out.println("Original string: " + str);

        // 获取Unsafe实例
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe) field.get(null);

        // 获取字符数组的首地址和长度,修改数组中某个字符的值
        long stringBaseOffset = unsafe.arrayBaseOffset(char[].class);
        long addressOfFirstChar = unsafe.getLong(str, stringBaseOffset);
        char[] chars = (char[]) unsafe.getObject(str, stringBaseOffset);
        chars[7] = 'J';

        // 重新构造字符串
        String newStr = (String) unsafe.allocateInstance(String.class);
        unsafe.putLong(newStr, stringBaseOffset, addressOfFirstChar);
        System.out.println("New string: " + newStr);
    }
}

运行结果:

Original string: Hello, World!
New string: Hello, Jorld!

此示例展示了可以使用 Unsafe 遍历并修改字符串的字符,以及如何重新构造一个字符串对象。

示例2:破解懒汉式单例模式

public class LazySingleton {
    private static volatile LazySingleton instance;
    private LazySingleton() {}

    public static LazySingleton getInstance() {
        if (instance == null) {
            synchronized (LazySingleton.class) {
                if (instance == null) {
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }
}

通过指针去创建对象的方式可以避免 synchronized 存在的锁消耗,但也会带来多对象被实例化的风险。这种方式并不能保证只会创建一个对象,只是此处概率较小。

public class ReflectionCreateObj implements Serializable {
    private static LazySingleton instance;

    static {
        Field field;
        try {
            field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            Unsafe unsafe = (Unsafe) field.get(null);

            // 获取LazySingleton对象的引用地址
            long instanceOffset = unsafe.objectFieldOffset(
                    ReflectionCreateObj.class.getDeclaredField("instance"));

            // 修改对象引用值
            UnsafeReflectionTest uninstallObj = new UnsafeReflectionTest();
            unsafe.putOrderedObject(uninstallObj, instanceOffset, LazySingleton.getInstance());
        } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    public static LazySingleton getInstance() {
        return instance;
    }
}

此示例展示了通过 Unsafe 修改了 LazySingleton 对象的引用地址,破解了懒汉式单例模式。这种方式可以恶意绕过系统的业务逻辑,窃取并篡改敏感数据。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java Unsafe详细解析 - Python技术站

(0)
上一篇 2023年5月23日
下一篇 2023年5月23日

相关文章

  • Autoruns怎么用?Autoruns详细图文教程

    Autoruns是一款系统工具软件,它可以用来查看Windows操作系统启动时会自动运行的进程,服务,驱动程序以及其他自启动项。下面将为大家提供一份Autoruns详细图文教程,让大家了解如何使用它。 Autoruns怎么用? 首先下载Autoruns软件并安装,这里提供官方下载地址:https://docs.microsoft.com/en-us/sysi…

    C 2023年5月23日
    00
  • 详解C++异常处理机制示例介绍

    以下是详解“详解C++异常处理机制示例介绍”的攻略。 异常处理机制介绍 异常处理是指程序在运行时出现异常情况(如除数为零、内存分配失败、文件不存在等)时,一种用来进行错误处理的机制,目的是确保程序能够继续正常执行而不被终止。 在C++中,异常处理机制分为三个部分:try、catch和throw。当程序出现异常时,会抛出异常对象,然后程序在try块中寻找匹配的…

    C 2023年5月23日
    00
  • C语言将音视频时钟同步封装成通用模块的方法

    在C语言中将音视频时钟同步封装成通用模块主要可以分为以下几个步骤: 1. 定义时钟同步结构体 使用结构体保存相关的时钟同步数据,如音视频播放起始时间、时钟周期、当前时钟时间等信息,方便后续模块进行时钟同步计算和状态管理。 typedef struct { int64_t start_pts; // 音视频播放起始时间,单位:微秒 int64_t num; /…

    C 2023年5月23日
    00
  • C++中的类成员函数当线程函数

    C++中的线程库(std::thread)可以处理各种类型的函数作为线程函数,包括类的成员函数。对于类成员函数,我们需要考虑如何处理this指针,并注意线程的生命周期。 以下是将类成员函数作为线程函数的完整攻略: 步骤1:定义类 首先,需要定义一个含有成员函数的类。本例中,我们定义了一个简单的Counter类,它具有公共函数increment(),用于增加计…

    C 2023年5月22日
    00
  • Linux网络编程之UDP Socket程序示例

    下面是关于使用UDP Socket进行Linux网络编程的攻略及示例. UDP Socket编程简介 UDP全称User Datagram Protocol,是一种无连接的,不可靠的面向数据报的传输协议,采用UDP传输需要自行保证数据的可靠性和完整性。因为UDP通信无连接,所以它发送的数据报文既不需要建立连接,也不需要断开连接,数据报文也不需要发送端和接收端…

    C 2023年5月30日
    00
  • Go中使用gjson来操作JSON数据的实现

    下面是详细讲解“Go中使用gjson来操作JSON数据的实现”的完整攻略: 一、什么是 gjson? GJSON是一个用于Go编程语言的JSON解析器,它提供了对JSON数据的快速和灵活访问。GJSON非常轻量级,只有一个单一的文件,没有外部依赖性。 二、如何使用 gjson? 2.1 安装 gjson 在使用gjson库之前,需要先安装其库文件。如果你使用…

    C 2023年5月23日
    00
  • Ajax和Comet技术总结

    Ajax和Comet技术总结 Ajax是Asynchronous JavaScript and XML(异步JavaScript和XML)的缩写。它是一种利用JavaScript和XML在Web服务器上异步传输数据的技术。它可以在不重新加载整个网页的情况下,更新某个特定部分的内容。Ajax能够在不干扰客户端的情况下,优化Web应用的性能。 Ajax的优点 网…

    C 2023年5月23日
    00
  • C++ 动态内存管理详情解说

    C++ 动态内存管理详情解说 在 C++ 程序中,动态内存管理是一项非常重要的任务。动态内存分配和释放可以在运行时动态地完成,使程序具有更大的灵活性。本文将详细解释动态内存管理的概念以及它的使用方法。 什么是动态内存? 动态内存是指程序在运行时动态地分配的内存。每个程序都有一个静态内存,该内存是编译时分配的。静态内存的大小是固定的,而动态内存的大小不是固定的…

    C 2023年5月22日
    00
合作推广
合作推广
分享本页
返回顶部