首页 > 编程语言 >Java中的Unsafe类

Java中的Unsafe类

时间:2023-02-27 08:44:35浏览次数:38  
标签:Java nums int Unsafe long public native

Java中的Unsafe类

Unsafe类时sun,misc包下的一个类,主要用于执行一些 Native 方法,同时它赋予了 Java 直接操作内存的能力,但是直接操作内存的能力同时也破坏了Java的安全性,可能这就是为什么叫 Unsafe 吧

关于Native 方法

Native 方法是没有方法体的,是为了调用其他语言写的( C/C++ )代码而写的函数,如果了解 JVM 的话,就知道线程内部不仅有虚拟机栈,还有一个本地方法栈,虚拟机栈是用来存放非本地方法调用时的栈帧,本地方法栈就是存放本地方法调用时的栈帧

Unsafe 获取

Unsafe类中定义了一个 getUnsafe() 方法,但是如果直接调用会抛出如下异常

Exception in thread "main" java.lang.SecurityException: Unsafe

这是因为 Unsafe 源码中 getUnsafe() 方法是如下定义

@CallerSensitive
public static Unsafe getUnsafe() {
    Class<?> caller = Reflection.getCallerClass();
    if (!VM.isSystemDomainLoader(caller.getClassLoader()))
        throw new SecurityException("Unsafe");
    return theUnsafe;
}

// 可以看到会先判断是不是系统类加载器调用的该方法,否则抛出SecurityException异常

直接获取不了只能使用反射的方法获取
在 Unsafe 类中定义了如下变量 ( theUnsafe )

private static final Unsafe theUnsafe = new Unsafe();

所以可以直接使用如下方式获取到 Unsafe 类对象

private static Unsafe unsafe;

private void init() throws NoSuchFieldException, IllegalAccessException {
    Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
    theUnsafe.setAccessible(true);
    unsafe = (Unsafe) theUnsafe.get(null);
}
Unsafe 中常用的方法
//分配新的本地空间
public native long allocateMemory(long bytes);
//重新调整内存空间的大小
public native long reallocateMemory(long address, long bytes);
//将内存设置为指定值
public native void setMemory(Object o, long offset, long bytes, byte value);
//内存拷贝
public native void copyMemory(Object srcBase, long srcOffset,Object destBase, long destOffset,long bytes);
//清除内存
public native void freeMemory(long address);


// 获取对象属性偏移量
public native long objectFieldOffset(Field field);
// 获取对象静态属性偏移量
public native long staticFieldOffset(Field field);
// 获取静态对象的基址
public Object staticFieldBase(Field field);


//获取数组的基址
public native int arrayBaseOffset(Class<?> arrayClass);
//获取数组的元素占用大小
public native int arrayIndexScale(Class<?> arrayClass);


// 比较并更新一个Object属性(CAS操作)
public final native boolean compareAndSwapObject(Object o, long offset,  Object expected, Object update);
public final native boolean compareAndSwapInt(Object o, long offset, int expected,int update);
public final native boolean compareAndSwapLong(Object o, long offset, long expected, long update);
...
练习

给一个题目:轮转数组

给定一个数组,将数组中的元素向左移动 k 个位置,其中 k 是非负数。
输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [4,5,6,7,1,2,3]

关于这一题其实有多种解法,但是这里主要尝试使用Unsafe类来解这个题
给定如下思路:
创建一个nums二倍长度的数组,存放两次nums中的数
例:

nums = {1,2,3,4,5,6}, k = 3
// 两倍数组并赋值后
nums2 = {1,2,3,4,5,6,1,2,3,4,5,6}
// 现在要得到目标数组,只需要从 nums2 中的第 k + 1 个元素开始截取,即可获得
nums2 => {1,2,3, | 4,5,6,1,2,3 | 4,5,6} => {4,5,6,1,2,3}

实现如下:

public int[] solution(int[] nums,int k) throws IllegalAccessException, NoSuchFieldException {
      // 扩容后的数组
      int[] nums2 = new int[nums.length * 2];
      int len = nums.length;
      // 利用反射获取 Unsafe 类
      Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
      theUnsafe.setAccessible(true);
      Unsafe unsafe = (Unsafe) theUnsafe.get(null);
      // 获取int[]的偏移地址
      int offset = unsafe.arrayBaseOffset(int[].class);
      // 获取int[]中每个元素的大小
      int size = unsafe.arrayIndexScale(int[].class);
      // 将nums中的元素内容复制到nums2中,复制两次让nums2 为两次 nums连接
      unsafe.copyMemory(nums,offset,nums2,offset,size * len);
      unsafe.copyMemory(nums,offset, nums2 ,offset + size * len, size * len);
      // 从第 k 个元素开始取 len 个元素复制回 nums
      unsafe.copyMemory(nums2,offset + k * size,nums, offset ,size * len);
      return nums;
  }

标签:Java,nums,int,Unsafe,long,public,native
From: https://www.cnblogs.com/pupilxiao/p/17158209.html

相关文章

  • 读Java性能权威指南(第2版)笔记03_ Java SE API技巧中
    1. 缓冲I/O1.1. 对于文件和套接字,压缩和字符串编码的操作,必须适当地对I/O进行缓冲1.1.1. 两个流操作的是字节块(来自缓冲流)而不是一系列的单字节(来自ObjectOutputStre......
  • Java基础——(综合练习)普通加密
    packagecom.zhao.test;publicclassTest18{/*需求:​某系统的数字密码(大于0),比如1983,采用加密方式进行传输。规则如下:......
  • Java基础——(综合练习)选手打分
    packagecom.zhao.test;importjava.util.Scanner;publicclassTest17{/*需求:在唱歌比赛中,有6名评委给选手打分,分数范围是[0-100]之间的整数。选......
  • java扫描指定注解的工具类
    publicclassBaseClassUtils{privatestaticfinalStringresource_pattern="/*.class";publicstaticMap<String,Class>scanMyComponentAnnotation(Str......
  • java学习日记20230227-java学习方法/转义字符/注释
    Java学习方法学习java基本原理和基本语法快速入门(基本程序CRUD)研究技术的注意事项,使用细节,使用规范,如何优化JAVA转义字符\t:一个制表位,实现对......
  • java面试考题小记
    1.在java中各种数据的默认值整数(byte、short、int、long)的默认值是:0;浮点数(float、double)的默认值是:0.0;字符(char)的默认值是:空格;布尔(boolean)的默认值:false;引用类型(arra......
  • java学习日记20230226-java环境搭建及运行机制
    JDK安装配置环境变量:当执行的程序在当前目录不存在时,windows去系统path环境变量里面进行查找,如果没有找到报错不存在该命令。我的电脑-属性-高级系统设置-......
  • Java对象的创建过程
    1)类加载检查:虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到这个类的符号引用,并且检查这个符号引用代表的类是否已被加载过、解析和初始化过。......
  • JavaSE5️⃣核心类 - 枚举(enum)
    1、枚举1.1、含义维基百科在数学和计算机科学理论中,一个集的枚举是指:列出有穷序列集的所有成员的程序。一种特定类型对象的计数。这两种类型经常重叠,是一个被命......
  • JAVA语言基础第一天
    1:Java开发环境编译运行过程:编译期:Java源文件,经过编译,生成.class字节码文件  运行期:JVM加载.class并运行.class(0和1) 特点:跨平台、一次编译到处使用......