一、简介
Unsafe类使Java拥有了像C语言的指针一样操作内存空间的能力,同时也带来了指针的问题。过度的使用Unsafe类会使得出错的几率变大,因此Java官方并不建议使用的。
二、unsafe类各方法介绍
unsafe类大部分是native方法,内容对应jdk源码中的unsafe.cpp文件,主要包含:
1、系统相关
主要返回某些低级别的内存信息。如:
- addressSize()返回系统指针大小
- pageSize()返回内存页大小
2、对象操作
主要提供Object和它的域操纵方法,如:
- allocateInstance(Class)创建一个对象
- objectFieldOffset(Field)获取对象某个属性的地址偏移值
3、Class相关
主要提供Class和它的静态域操纵方法,如:
- staticFieldOffset(Field)获取给定静态字段的内存地址偏移量
- staticFieldBase(Field)获取一个静态类中给定字段的对象指针
- defineClass(xxx)定义一个类,此方法会跳过JVM的所有安全检查
- defineAnonymousClass(xxx)定义一个匿名类,默认情况下,ClassLoader(类加载器)和ProtectionDomain(保护域)实例来源于调用者
- ensureClassInitialized(Class)检测给定的类是否已经初始化。通常在获取一个类的静态属性的时候(因为一个类如果没初始化,它的静态属性也不会初始化)使用。
- shouldBeInitialized(Class)判断是否需要初始化一个类,通常在获取一个类的静态属性的时候(因为一个类如果没初始化,它的静态属性也不会初始化)使用。 当且仅当ensureClassInitialized方法不生效时返回false。
4、数组相关
数组操纵方法,如:
- arrayBaseOffset(Class)返回数组中第一个元素的偏移地址
- arrayIndexScale(Class)返回数组中一个元素占用的大小
5、Memory相关
直接内存访问方法(绕过JVM堆直接操纵本地内存),如:
- allocateMemory(long)分配一段long长度的内存空间
- reallocateMemory(long var1, long var3)重新给var1起始地址的内存分配长度为var3字节的内存,返回新的内存起始地址偏移量
- setMemory(XXX)将给定内存块中的所有字节设置为固定值(通常是0)。
- copyMemory(XXX)复制内存
- freeMemory(long)释放内存
- putXXX(XXX)设置给定对象的XXX值(如int值)
- getXXX(XXX);获得给定对象的指定偏移量offset的XXX值(如int值)
- putXXXVolatile(XXX)设置给定对象的XXX值(如int值),使用volatile语义,即设置后立马更新到内存对其他线程可见
- getXXXVolatile(XXX);获得给定对象的指定偏移量offset的XXX值(如int值),使用volatile语义,总能获取到最新的XXX值(如int值)。
- putOrderedInt(Object var1, long var2, int var4)(将整数写入指定内存地址、有序或者有延迟的方法)
6、cas操作
- compareAndSwapInt(Object var1, long var2, int var4, int var5)针对Object对象进行CAS操作。比较并交换,更新成功返回true,否则返回false。
- compareAndSwapXXX(XXX)其他类型
7、线程调度
- park(boolean var1, long var2)线程挂起
- unpark(Object var1)线程恢复
8、内存屏障
- loadFence()在该方法之前的所有读操作,一定在load屏障之前执行完成。
- storeFence()在该方法之前的所有写操作,一定在store屏障之前执行完成
- fullFence()在该方法之前的所有读写操作,一定在full屏障之前执行完成,这个内存屏障相当于上面两个(load屏障和store屏障)的合体功能
9、多线程同步
- monitorEnter(Object var1)锁定对象,必须通过
monitorExit
方法才能解锁。此方法经过实验是可以重入的,也就是可以多次调用,然后通过多次调用monitorExit
进行解锁。 - monitorExit(Object var1)解锁对象,前提是对象必须已经调用
monitorEnter
进行加锁,否则抛出IllegalMonitorStateException异常 - tryMonitorEnter(Object var1)尝试锁定对象,如果加锁成功返回true,否则返回false。必须通过
monitorExit
方法才能解锁。
10、其他
- getLoadAverage(double[] var1, int var2)获取系统的平均负载值
- throwException(Throwable ee)绕过检测机制直接抛出异常
三、unsafe类java获取
通过反射
try {
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
Unsafe unsafe= (Unsafe) theUnsafeField.get(null);
System.out.println(unsafe);
} catch (Exception e) {
}
四、unsafe应用
1、compareAndSwapInt方法的cas操作可以实现高效的无锁结构
2、分配堆外内存,绕开jvm gc管理提高性能。(直接使用java.nio.DirectByteBuffer类即可)
注意:忘记手动回收内存会产生内存泄漏。非法地址访问会导致jvm崩溃。需要分配大的连续空间、实时编程(不能容忍jvm延迟)如 java.nio常用
3、动态加载类,除了Class.forName()之外使用unsafe也能加载类。
4、创建对象时绕开构造方法。使用allocateInstance就能实现
5、内存数据修改。通过计算内存偏移量,再使用putInt方法就可直接修改字段的值
6、计算对象大小。可以通过objectFieldOffset就可以实现类似于c语言的sizeOf()函数功能,加上allocateMemory和copyMemory也能实现对象的浅复制
7、挂起、唤醒线程。park、unpark用的最多