Unsafe类
在 Java 中,Unsafe
类是一个位于 sun.misc
包下的内部类,提供了一些底层的、与 JVM 和操作系统密切相关的功能。Unsafe
类的主要用途是访问和操作内存以及线程管理等低级功能,这些功能通常是常规 Java API 所不允许的,因此具有较高的危险性(也就是为什么称其为 “unsafe”)。
Unsafe
类中的大多数方法都不执行常规的安全检查,因此能够绕过 Java 的内存管理和对象模型。这也是为什么它通常只被 Java 内部或一些性能关键的库(如并发库)所使用。
另外,Unsafe
提供的这些功能的实现需要依赖本地方法(Native Method)。你可以将本地方法看作是 Java 中使用其他编程语言编写的方法。本地方法使用 native
关键字修饰,Java 代码中只是声明方法头,具体的实现则交给 本地代码。
为什么要使用本地方法呢?
- 需要用到 Java 中不具备的依赖于操作系统的特性,Java 在实现跨平台的同时要实现对底层的控制,需要借助其他语言发挥作用。
- 对于其他语言已经完成的一些现成功能,可以使用 Java 直接调用。
- 程序对时间敏感或对性能要求非常高时,有必要使用更加底层的语言,例如 C/C++甚至是汇编
主要功能:
-
内存管理:
allocateMemory(long size)
:分配一块指定大小的内存,类似于 C/C++ 中的malloc
。freeMemory(long address)
:释放通过allocateMemory
分配的内存。reallocateMemory(long address, long newSize)
:重新分配已经分配的内存大小。putLong/putInt/putObject/putDouble(long address, value)
:向指定的内存地址写入数据。getLong/getInt/getObject/getDouble(long address)
:从指定的内存地址读取数据。
-
对象操作:
objectFieldOffset(Field field)
:获取某个对象字段的内存偏移量,主要用于反射和字段操作。compareAndSwapObject/compareAndSwapInt/compareAndSwapLong
:提供原子操作(CAS,Compare-And-Swap),用于实现无锁算法和并发操作。putObjectVolatile/getObjectVolatile
:获取或设置对象字段,支持内存屏障(volatile)的语义,确保线程间的可见性。
-
线程控制:
park(boolean isAbsolute, long time)
:挂起当前线程。unpark(Thread thread)
:唤醒某个线程。loadFence/storeFence/fullFence
:内存屏障操作,用于多线程编程中的内存可见性保证。
-
数组操作:
arrayBaseOffset(Class<?> arrayClass)
:获取数组的基础偏移量,通常是第一个元素的地址。arrayIndexScale(Class<?> arrayClass)
:获取数组元素的地址步长。
-
类加载:
defineClass
:允许动态加载类。
注意事项:
由于 Unsafe
类的危险性和对 JVM 内存结构的直接操作,普通 Java 应用程序开发中通常不应该直接使用这个类。它绕过了 Java 提供的安全机制,并且错误的使用可能会导致应用程序崩溃、内存泄漏甚至是不可预料的行为。
另外,Java 9 开始,Unsafe
类被封装并非公开 API 使用。虽然可以通过反射等手段获取 Unsafe
实例,但这是一种不被推荐的做法。Java 引入了其他更安全的机制来替代 Unsafe
类的一些功能,如 VarHandles 和 java.util.concurrent
包中的原子类等。
示例:
以下是一个通过反射获取 Unsafe
实例并分配内存的简单示例:
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class UnsafeExample {
public static void main(String[] args) throws Exception {
// 通过反射获取Unsafe实例
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
// 分配内存
long memoryAddress = unsafe.allocateMemory(8); // 分配8个字节的内存
unsafe.putLong(memoryAddress, 12345678L); // 向分配的内存写入数据
long value = unsafe.getLong(memoryAddress); // 从内存读取数据
System.out.println("Value: " + value);
unsafe.freeMemory(memoryAddress); // 释放内存
}
}
补充
Bootstrap ClassLoader
(引导类加载器)是 Java 虚拟机(JVM)中最基础的类加载器,负责加载核心 Java 类库。它是 JVM 自带的,并且直接由 JVM 实现,而不是用 Java 编写的。
Java 类加载器层次结构
在 Java 中,类加载器的工作机制遵循双亲委派模型,按照优先级划分为三个主要类加载器:
-
Bootstrap ClassLoader(引导类加载器)
- 作用:负责加载 JVM 自带的核心类库,比如
java.lang.*
、java.util.*
、java.io.*
等。这些类通常位于JAVA_HOME/lib
目录下的rt.jar
文件中。 - 语言实现:Bootstrap ClassLoader 不是由 Java 编写的,它是用本地代码实现的(C/C++),直接集成在 JVM 中。
- 特点:由于它是 JVM 自带的,所以开发者无法直接获取或操作它,且其加载的类位于 Java 的核心类库之中。
- 作用:负责加载 JVM 自带的核心类库,比如
-
Extension ClassLoader(扩展类加载器)
- 作用:负责加载扩展类库,通常位于
JAVA_HOME/lib/ext
目录中的 JAR 包。这些类库为标准的 Java 类库提供扩展功能。 - 实现:由 Java 实现,继承自
ClassLoader
类。 - 特点:可以通过
ClassLoader.getParent()
方法访问到。
- 作用:负责加载扩展类库,通常位于
-
Application ClassLoader(应用程序类加载器)
- 作用:负责加载应用程序的类文件,通常加载我们编写的代码(classpath 中指定的类)。它加载的类通常位于项目的
classpath
路径下。 - 实现:由 Java 实现,也是我们日常使用的类加载器。通常通过
Thread.currentThread().getContextClassLoader()
获取。 - 特点:可以由开发者直接操作和管理。
- 作用:负责加载应用程序的类文件,通常加载我们编写的代码(classpath 中指定的类)。它加载的类通常位于项目的
双亲委派模型
双亲委派模型规定:类加载器在加载类时,先将任务委派给它的父类加载器,直到 Bootstrap ClassLoader
为止。只有当父类加载器无法找到指定的类时,子类加载器才会尝试自己加载。这种机制确保了 Java 核心类库的安全性和唯一性。
加载过程示意:
- 当
Application ClassLoader
要加载一个类时,首先把请求委托给它的父类Extension ClassLoader
。 Extension ClassLoader
会再把请求委托给Bootstrap ClassLoader
。Bootstrap ClassLoader
尝试从核心类库中加载类,如果成功则返回,否则把控制权交回给Extension ClassLoader
。- 如果
Extension ClassLoader
无法加载,则最终由Application ClassLoader
尝试加载。
Bootstrap ClassLoader 的特点
- 不可见:由于
Bootstrap ClassLoader
是由 JVM 实现的,使用 Java API 不能直接获取其实例。例如,调用Object.getClass().getClassLoader()
会返回null
,表示该类是由Bootstrap ClassLoader
加载的。 - 加载核心类库:
Bootstrap ClassLoader
主要负责加载核心的 JDK 类库。通常它从JAVA_HOME/lib
下的rt.jar
(JDK 8 及之前版本)等文件中加载类。 - 不继承
ClassLoader
:Bootstrap ClassLoader
不继承自ClassLoader
,而是使用本地代码实现。因此它的行为与其他类加载器不同。
示例:
当你运行以下代码时,java.lang.Object
是由 Bootstrap ClassLoader
加载的,返回的结果是 null
。
public class ClassLoaderTest {
public static void main(String[] args) {
ClassLoader classLoader = Object.class.getClassLoader();
System.out.println(classLoader); // 输出 null
}
}
总结
Bootstrap ClassLoader
是 JVM 自带的引导类加载器,负责加载核心 Java 类库,无法直接通过 Java 代码访问或操作它。它处于整个类加载器层次结构的顶层,通过双亲委派机制确保了 Java 核心类库的安全和稳定性。