Java 魔法类 Unsafe 源码解读(一)
前言
阅读过 JUC
源码的同学,一定会发现很多并发工具都调用了一个叫做 Unsafe
的类。
那这个类的作用是什么呢?有什么使用场景呢?底层源码是什么样呢?这篇文章笔者就带你搞清楚!
本文章所使用的是 JDK11
。不同于市面上大部分的文章所使用的JDK8
。
希望各位在阅读我这篇文章时,静下心来,逐字逐句的阅读!
同时建议大家可以点开 Unsafe
这个类,跟着我的文章一起深入它。
一、Unsafe 介绍
Unsafe
是位于 sum.misc
包下的一个类(其实真正的 Unsafe
是位于 jdk.internal.misc
包下的,笔者后面会说到。)主要提供一些用于执行低级别、不安全操作的方法,如直接访问系统内存资源、自主管理内存资源等,这些方法在提升 Java 运行效率、增强 Java 语言底层资源操作能力方面起到了很大的作用。但由于 Unsafe
类使 Java 语言拥有了类似 C 语言指针一样操作内存空间的能力,这无疑也增加了程序发生相关指针问题的风险。
在程序中过度、不正确使用 Unsafe
类会使得程序出错的概率变大,使得 Java 这种安全的语言变得不再“安全”,因此对 Unsafe
的使用一定要慎重!
另外,Unsafe
提供的这些功能的实现需要依赖本地方法(Native Method)。
- 什么是本地方法? -> 本地方法可以看作是 Java 中使用其他编程语言编写的方法。本地方法使用
native
关键字修饰,Java 代码中只是声明方法头,具体的实现则交给本地代码。
扩展:
为什么要使用本地方法呢?
1.需要用到 Java 中不具备的依赖于操作系统的特性,Java 在实现跨平台的同时要实现对底层的控制,需要借助其他语言发挥作用。
2.对于其他语言已经完成的一些现成功能,可以使用 Java 直接调用。
3.程序对时间敏感或对性能要求非常高时,有必要使用更加底层的语言,例如 C/C++甚至是汇编。
在 JUC 包的很多并发工具类在实现并发机制时,都调用了本地方法,通过它们打破了 Java 运行时的界限,能够接触到操作系统底层的某些功能。对于同一本地方法,不同的操作系统可能会通过不同的方式来实现,但是对于使用者来说是透明的,最终都会得到相同的结果。
二、Unsafe 创建
首先,我们从 Unsafe
类的定义来解读源码。
public final class Unsafe {
// 静态代码块,会在程序启动时最先执行且只会执行一次。
// 目的不明,要是有知道的大佬可以解答下!!!
static {
Reflection.registerMethodsToFilter(Unsafe.class, "getUnsafe");
}
private Unsafe() {}
// 单例对象 饿汉式单例
private static final Unsafe theUnsafe = new Unsafe();
private static final jdk.internal.misc.Unsafe theInternalUnsafe = jdk.internal.misc.Unsafe.getUnsafe();
// 核心代码!
@CallerSensitive
public static Unsafe getUnsafe() {
// 返回调用此方法的调用者的类
Class<?> caller = Reflection.getCallerClass();
// 仅在引导类加载器 BootstrapClassLoader 或平台类加载器 PlatformClassLoader 加载时才合法
if (!VM.isSystemDomainLoader(caller.getClassLoader()))
throw new SecurityException("Unsafe"); // 如果调用者类的类加载器不在授予所有权限的系统域中,抛出此异常。
return theUnsafe;
}
}
// 如果给定的类加载器是引导类加载器 BootstrapClassLoader 或平台类加载器 PlatformClassLoader,则返回true。
public static boolean isSystemDomainLoader(ClassLoader loader) {
// ClassLoader最上方的类定义中规定了 BootstrapClassLoader 和 PlatformClassLoader 的定义
// BootstrapClassLoader:它是虚拟机的内置类加载器,通常表示为null,并且没有父类。
// PlatformClassLoader:所有平台类对于可用作ClassLoader实例父级的平台类加载器都是可见的。平台类包括Java SE平台API、它们的实现类和由平台类加载器或其祖先定义的JDK特定运行时类。
return loader == null || loader == ClassLoader.getPlatformClassLoader();
}
Unsafe
类为饿汉单例实现,提供静态方法 getUnsafe
获取 Unsafe
实例。这个看上去貌似可以用来获取 Unsafe
实例。但是,当我们直接通过 Unsafe.getUnsafe()
调用这个静态方法的时候,会抛出 SecurityException
异常!
// 这是因为 VM.isSystemDomainLoader() 进行了检查。
// 会对调用者的classLoader进行检查,判断当前类是否由 BootstrapClassLoader 加载,如果不是的话那么就会抛异常。
Exception in thread "main" java.lang.SecurityException: Unsafe
at sun.misc.Unsafe.getUnsafe(Unsafe.java:90)
at com.cn.test.GetUnsafeTest.main(GetUnsafeTest.java:12
三、Unsafe 操作
Unsafe
的操作主要可以分为以下几类:
- 内存操作
- 内存屏障
- 对象操作
- 数据操作
- CAS 操作
- 线程调度
- Class 调度
- 系统信息
内存操作
Java
中是不允许直接对内存进行操作的,对象内存的分配和回收都是由 JVM
自己实现的(自动内存管理垃圾回收机制 GC
)。
但是在 Unsafe
中,提供了以下几个接口来直接操作内存:
// 分配给定大小(以字节为单位)的新本地内存块
public long allocateMemory(long bytes);
// 将新的本地内存块调整为给定的字节大小。
public long reallocateMemory(long address, long bytes);
//将内存设置为指定值
public void setMemory(Object o, long offset, long bytes, byte value);
//内存拷贝
public void copyMemory(Object srcBase, long srcOffset,Object destBase, long destOffset,long bytes);
//清除内存
public void freeMemory(long address);
后记
剩下的内容,笔者会在三天内出完。各位可以期待下。这篇文章有学习JavaGuide的部分内容!