Java魔法类 Unsafe
JUC源码中的并发工具类出现过很多次 Unsafe类,它
的功能以及使用场景这篇进行介绍。
文章导读:(约12015字,阅读时间大约1小时)
1. Unsafe介绍
Unsafe是位于sun.misc包下的类,提供一些更底层的,访问系统内存资源,和管理系统内存资源的方法,但是因为会访问系统的内存资源 变成和C语言一样的指针,指针的使用是有风险的,所以Unsafe也是有类似的风险,所以在使用的时候需要注意,过度或者不正确的使用可能导致程序出错。
但是,Unsafe类也使得Java增强了底层操作系统资源的能力。
同时,Unsafe提供的功能的实现是依赖于本地方法(Native Method)的,本地方法就是Java中使用其他语言写的方法,本地方法用native修饰,java只声明方法,具体实现由本地方法实现。
使用本地方法的原因:
- 需要使用到Java没有的特性,就得使用本地方法 来用别的语言来实现,比如Java没有什么底层操作系统的能力,所以要想在跨平台的同时还可以由底层控制的能力,就要使用本地方法。
- 其他语言已经实现的功能,可以Java调用使用
- Java在速度上比一些更底层的语言慢,如果程序对时间要求高或者对性能要求高,那么就需要使用更底层的语言。
JUC包的很多并发工具类在实现并发功能的时候,都调用了本地方法,用来提高Java的运行上限,同时为了能更底层的操作操作系统,也会使用本地方法,对于本地方法来说,不同操作系统的实现不太一样,但是使用起来是一样的。
2. Unsafe创建
public final class Unsafe {
// 单例对象
private static final Unsafe theUnsafe;
......
private Unsafe() {
}
@CallerSensitive
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
// 仅在引导类加载器`BootstrapClassLoader`加载时才合法
if(!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}
}
Unsafe类是单例实现,可以通过静态getUnsafe方法获取实例。但是有个前提是 在调用getUnsafe方法的时候会对调用者的ClassLoader进行检查,也就是检查类加载器,如果是由Bootstrap classLoader加载的,那么才可以获得实例 如果不是那么就抛出异常SecurityException,所以,只有启动类加载器加载的类才可以调用Unsafe类中的方法,有助于避免被不可信代码调用,(因为这个原因说一在获取Unsafe类的实例的时候 大概率会抛出SecurityException异常,这样就要有别的方法来获取实例,比如下面讲到的反射方法)
那么为什么使用Unsafe类这么有限制?
Unsafe类提供的功能很底层,它可以访问系统资源,操作系统资源,所以存在一些安全风险,
在这个限制的条件下如何来获取Unsafe实例呢
我知道的方法是一个:
通过反射
我们可以通过反射来获得Unsafe类中以及完成实例化的theUnsafe对象
//通过反射获取Unsafe类中已经实例化完成的theUnsafe对象
private static Unsafe reflectGetUnsafe() {
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
return (Unsafe) field.get(null);
} catch (NoSuchFieldException e) {
//处理异常
e.printStackTrace();
return null;//返回内容
} catch (IllegalAccessException e) {
e.printStackTrace();
return null;
}
}
3. Unsafe功能
3.1内存操作
Java中不能直接操作内存,对象的分配内存和释放都是JVM完成的,在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 void Test() {
Unsafe unsafe = reflectGetUnsafe();
int size = 4;
long addr = unsafe.allocateMemory(size);
long addr1 = unsafe.reallocateMemory(addr,size*2);
System.out.println("addr:" + addr);
System.out.println("addr1:" + addr1);
try {
unsafe.setMemory(null,addr,size,(