java Unsafe详细解析

yizhihongxing

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日

相关文章

  • php自定义中文字符串截取函数substr_for_gb2312及substr_for_utf8示例

    下面我将为您详细讲解“php自定义中文字符串截取函数substr_for_gb2312及substr_for_utf8示例”的攻略。 分析问题 中文字符串截取是一个比较常见的需求,但是在PHP中的substr函数并不支持中文字符集,如果直接使用原生substr函数截取中文字符串会导致出现乱码或者截取不准确的问题。所以我们需要自定义中文字符串截取函数来解决这个…

    C 2023年5月22日
    00
  • C语言顺序查找算法介绍及示例

    C语言顺序查找算法介绍及示例攻略 什么是顺序查找算法? 顺序查找算法,也叫线性查找算法,是一种最基本的查找算法。是通过一次次的比较目标值与列表中的每一个元素来实现查找的。 顺序查找算法的实现方法 实现顺序查找,需要下面两个步骤: 从列表中的第一个元素开始,逐个与目标值进行比较,直到查找到目标值,或者搜索完整个列表。 如果在列表中找到了目标值,返回其在列表中的…

    C 2023年5月22日
    00
  • C语言 超详细讲解链接器

    C语言 超详细讲解链接器 什么是链接器 在C语言编写代码时,我们往往需要调用一些库函数,比如printf、malloc等等。这些库函数在我们的代码文件中并没有实现,而是存储在系统或其他库文件中,我们需要通过链接器把这些函数与我们编写的代码组合在一起,生成可执行程序。 链接器主要负责以下两个任务:- 符号解析:将目标文件中引用的符号与定义的符号建立联系。- 符…

    C 2023年5月23日
    00
  • 学习C和C++的9点经验总结

    学习C和C++的9点经验总结 1. 坚持理论和实践相结合 C和C++是一门理论性、实践性极强的编程语言。只有理论和实践相结合,才能够提高编程水平。因此,在学习过程中,需要注重理论和实践相结合,既要阅读相关的理论知识,也要进行实践操作。 示例:学习数据类型的时候,需要先阅读相关知识,再通过编写实例代码来加深理解。 #include<iostream&gt…

    C 2023年5月30日
    00
  • win7启动程序时弹出异常代码c0000005怎么办?

    下面是“win7启动程序时弹出异常代码c0000005”的完整攻略: 问题描述 在启动某些程序时,可能会遇到异常代码c0000005的错误提示,例如: 异常代码c0000005,详细信息是:ACCESS_VIOLATION 解决方案 方案一:更新或重装程序 可能是程序本身存在问题,建议先到官网下载最新版本安装或者尝试重装程序,看看能否解决问题。 方案二:检查…

    C 2023年5月23日
    00
  • C++中的函数指针与函数对象的总结

    以下是关于”C++中的函数指针与函数对象的总结”的详细攻略。 什么是函数指针? 函数指针其实就是指向函数的指针,它可以像普通指针一样进行声明、赋值、传递参数等操作。C++中的函数指针的语法形式为: 返回值类型 (*指针变量名)(参数类型列表); 举个例子,我们定义一个名为add的函数,它的作用是将两个整数相加并返回结果。那么我们可以这样声明一个函数指针变量:…

    C 2023年5月22日
    00
  • Java日常练习题,每天进步一点点(28)

    题目:给定两个字符串,找到这两个字符串中最长的公共连续子字符串。 示例1: 输入: str1 = “ABCD” ,str2 = “CBCE”输出: “BC” 示例2: 输入: str1 = “ABC” ,str2 = “DEF”输出: “” 分析:题目要求找到两个字符串的最长公共连续子字符串,我们可以通过动态规划算法来解决此类问题。具体思路是,定义一个二维数…

    C 2023年5月23日
    00
  • C++基本算法思想之穷举法

    C++基本算法思想之穷举法攻略 穷举法概述 穷举法是一种基本的算法思想,也称为暴力搜索或枚举搜索,是一种对所有可能性进行逐一验证的算法。它通过枚举问题所有可能的解,来寻找问题的最优解。 穷举法的具体步骤 穷举法的具体步骤可以分为三部分: 1. 确定问题的解空间 问题的解空间是指问题的所有可能解构成的集合。在使用穷举法解决问题时,需要确定问题的解空间,以便于后…

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