简介
顾名思义他是不安全的,他的功能如下图
@CallerSensitive
public static Unsafe getUnsafe() {
//利用这个可以获取调用者的类
Class var0 = Reflection.getCallerClass();
//这句话判断类的加载器是不是主类加载器,也就是ClassLoader为null
if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}
数组相关
arrayBaseOffset方法返回数组在内存中的偏移量,这个值是固定的。arrayIndexScale返回数组中的每一个元素的内存地址换算因子。举个栗子,double数组(注意不是包装类型)每个元素占用8个字节,所以换算因子为8,int类型则为4,通过这两个方法我们就能定位数组中每个元素的内存地址,从而赋值,下面代码演示:
public class arrayDemo {
public static void main(String[] args) throws Exception {
// 用反射 new 对象
Class<Unsafe> unsafeClass = Unsafe.class;
Constructor<Unsafe> constructor = unsafeClass.getDeclaredConstructor();
constructor.setAccessible(true);
Unsafe unsafe = constructor.newInstance();
Integer[] integers = new Integer[10];
// 打印数组的原始值
System.out.println(Arrays.toString(integers));
// [null, null, null, null, null, null, null, null, null, null]
// 获取Integer数组在内存中的固定的偏移量
long arrayBaseOffset = unsafe.arrayBaseOffset(Integer[].class);
System.out.println(unsafe.arrayIndexScale(Integer[].class));
// 4
System.out.println(unsafe.arrayIndexScale(double[].class));
// 8
// 将数组中第一个元素的更新为100
unsafe.putObject(integers, arrayBaseOffset, 100);
// 将数组中第五个元素更新为50 注意 引用类型占用4个字节,所以内存地址 需要 4 * 4 = 16
unsafe.putObject(integers, arrayBaseOffset + 16, 50);
// 打印更新后的值
System.out.println(Arrays.toString(integers));
// [100, null, null, null, 50, null, null, null, null, null]
}
}
输出
[null, null, null, null, null, null, null, null, null, null]
4
8
[100, null, null, null, 50, null, null, null, null, null]
内存屏障
//分配内存, 相当于C++的malloc函数
public native long allocateMemory ( long bytes);
//释放内存
public native void freeMemory ( long address);
//在给定的内存块中设置值
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 class demo1 {
public static void main(String[] args) throws Exception {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
// 分配 10M的堆外内存
long _10M_Address = unsafe.allocateMemory(1 * 1024 * 1024 * 10);
// 将10M内存的 前面1M内存值设置为10
unsafe.setMemory(_10M_Address, 1 * 1024 * 1024 * 1, (byte) 10);
// 获取第1M内存的值: 10
System.out.println(unsafe.getByte(_10M_Address + 1000));
// 获取第1M内存后的值: 0(没有设置)
System.out.println(unsafe.getByte(_10M_Address + 1 * 1024 * 1023 * 1));
System.out.println(unsafe.getByte(_10M_Address + 100 * 1 * 5));
// System.out.println(unsafe.getByte(_10M_Address + 100));
}
}
系统相关
线程调度
public class threadDemo {
public static void main(String[] args) throws Exception {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
/**
* 线程许可
* 许可线程通过(park),或者让线程等待许可(unpark),
*/
Thread packThread = new Thread(() -> {
long startTime = System.currentTimeMillis();
//纳秒,相对时间park
unsafe.park(false,3000000000L);
System.out.println("我三秒等完了");
//毫秒,绝对时间park
//unsafe.park(true,System.currentTimeMillis()+3000);
System.out.println("main thread end,cost :"+(System.currentTimeMillis()-startTime)+"ms");
});
System.out.println("hello");
packThread.start();
TimeUnit.SECONDS.sleep(1);
System.out.println("我说完了");
}
}
运行结果
hello
我说完了
我三秒等完了
main thread end,cost :3008ms
public class threadDemo {
public static void main(String[] args) throws Exception {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
if (i == 5) {
// i == 5时,将当前线程挂起
unsafe.park(false, 0L);
}
System.out.println(Thread.currentThread().getName() + " printing i : " + i);
}
}, " Thread__Unsafe__1");
t1.start();
// 主线程休息三秒
Thread.sleep(3000L);
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " printing i : " + i);
if (i == 9) {
// 将线程 t1 唤醒
unsafe.unpark(t1);
}
}
System.in.read();
}
}
Thread__Unsafe__1 printing i : 0
Thread__Unsafe__1 printing i : 1
Thread__Unsafe__1 printing i : 2
Thread__Unsafe__1 printing i : 3
Thread__Unsafe__1 printing i : 4
main printing i : 0
main printing i : 1
main printing i : 2
main printing i : 3
main printing i : 4
main printing i : 5
main printing i : 6
main printing i : 7
main printing i : 8
main printing i : 9
Thread__Unsafe__1 printing i : 5
Thread__Unsafe__1 printing i : 6
Thread__Unsafe__1 printing i : 7
Thread__Unsafe__1 printing i : 8
Thread__Unsafe__1 printing i : 9
内存操作
CAS
Class
对象操作
public class OperateObjectExample {
/**
* 1、public native Object allocateInstance(Class<?> var1); 分配内存
* 2、public native Class<?> defineClass(String var1, byte[] var2, int var3, int var4, ClassLoader var5, ProtectionDomain var6); 方法定义一个类用于动态的创建类
* @throws Exception
*/
public static void operateObjectUseUnsafe() throws Exception{
Unsafe unsafe = UnsafeFactory.getUnsafe();
// 使用Unsafe的allocateInstance()方法,可以无需使用构造函数的情况下实例化对象
User user = (User) unsafe.allocateInstance(User.class);
user.setId(1);
user.setName("李子捌");
System.out.println(user);
// 返回对象成员属性在内存中相对于对象在内存中地址的偏移量
Field name = User.class.getDeclaredField("name");
long fieldOffset = unsafe.objectFieldOffset(name);
// 使用Unsafe的putXxx()方法,可以直接修改内存地址指向的数据(可以越过权限访问控制符)
unsafe.putObject(user, fieldOffset, "李子柒");
System.out.println(user);
// 使用Unsafe在运行时通过.class文件,创建类
File classFile = new File("E:\workspaceall\liziba-javap5\out\production\liziba-javap5\com\liziba\unsafe\pojo\User.class");
FileInputStream fis = new FileInputStream(classFile);
byte [] classContent = new byte[(int) classFile.length()];
fis.read(classContent);
Class<?> clazz = unsafe.defineClass(null, classContent, 0, classContent.length, null, null);
Constructor<?> constructor = clazz.getDeclaredConstructor(int.class, String.class);
System.out.println(constructor.newInstance(1, "李子玖"));
}
public static void main(String[] args) {
try {
OperateObjectExample.operateObjectUseUnsafe();
} catch (Exception e) {
e.printStackTrace();
}
}
}
应用
创建对象
/**
* Java数组大小的最大值为Integer.MAX_VALUE。使用直接内存分配,我们创建的数组大小受限于堆大小;
* 实际上,这是堆外内存(off-heap memory)技术,在java.nio包中部分可用;
*
* 这种方式的内存分配不在堆上,且不受GC管理,所以必须小心Unsafe.freeMemory()的使用。
* 它也不执行任何边界检查,所以任何非法访问可能会导致JVM崩溃
*/
public class SuperArray {
private static Unsafe unsafe = null;
private static Field getUnsafe = null;
static {
try {
getUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
getUnsafe.setAccessible(true);
unsafe = (Unsafe) getUnsafe.get(null);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
private final static int BYTE = 1;
private long size;
private long address;
public SuperArray(long size) {
this.size = size;
address = unsafe.allocateMemory(size * BYTE);
}
public void set(long i, byte value) {
unsafe.putByte(address + i * BYTE, value);
}
public int get(long idx) {
return unsafe.getByte(address + idx * BYTE);
}
public long size() {
return size;
}
public static void main(String[] args) {
long SUPER_SIZE = (long)Integer.MAX_VALUE * 2;
SuperArray array = new SuperArray(SUPER_SIZE);
System.out.println("Array size:" + array.size()); // 4294967294
int sum=0;
for (int i = 0; i < 100; i++) {
array.set((long)Integer.MAX_VALUE + i, (byte)3);
sum += array.get((long)Integer.MAX_VALUE + i);
}
System.out.println(sum);
}
}
谢谢观看,关于Usafe待补全
有深浅拷贝案例: Java之Unsafe-越迷人的越危险 - 掘金 (juejin.cn)