Java Unsafe类

Unsafe类

在 Java 中,Unsafe 类是一个位于 sun.misc 包下的内部类,提供了一些底层的、与 JVM 和操作系统密切相关的功能。Unsafe 类的主要用途是访问和操作内存以及线程管理等低级功能,这些功能通常是常规 Java API 所不允许的,因此具有较高的危险性(也就是为什么称其为 “unsafe”)。

Unsafe 类中的大多数方法都不执行常规的安全检查,因此能够绕过 Java 的内存管理和对象模型。这也是为什么它通常只被 Java 内部或一些性能关键的库(如并发库)所使用。

另外,Unsafe 提供的这些功能的实现需要依赖本地方法(Native Method)。你可以将本地方法看作是 Java 中使用其他编程语言编写的方法。本地方法使用 native 关键字修饰,Java 代码中只是声明方法头,具体的实现则交给 本地代码

为什么要使用本地方法呢?

  1. 需要用到 Java 中不具备的依赖于操作系统的特性,Java 在实现跨平台的同时要实现对底层的控制,需要借助其他语言发挥作用。
  2. 对于其他语言已经完成的一些现成功能,可以使用 Java 直接调用。
  3. 程序对时间敏感或对性能要求非常高时,有必要使用更加底层的语言,例如 C/C++甚至是汇编

主要功能:

Refer:Java 魔法类 Unsafe 详解 | JavaGuide

  1. 内存管理:

    • 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):从指定的内存地址读取数据。
  2. 对象操作:

    • objectFieldOffset(Field field):获取某个对象字段的内存偏移量,主要用于反射和字段操作。
    • compareAndSwapObject/compareAndSwapInt/compareAndSwapLong:提供原子操作(CAS,Compare-And-Swap),用于实现无锁算法和并发操作。
    • putObjectVolatile/getObjectVolatile:获取或设置对象字段,支持内存屏障(volatile)的语义,确保线程间的可见性。
  3. 线程控制:

    • park(boolean isAbsolute, long time):挂起当前线程。
    • unpark(Thread thread):唤醒某个线程。
    • loadFence/storeFence/fullFence:内存屏障操作,用于多线程编程中的内存可见性保证。
  4. 数组操作:

    • arrayBaseOffset(Class<?> arrayClass):获取数组的基础偏移量,通常是第一个元素的地址。
    • arrayIndexScale(Class<?> arrayClass):获取数组元素的地址步长。
  5. 类加载:

    • 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 中,类加载器的工作机制遵循双亲委派模型,按照优先级划分为三个主要类加载器:

  1. Bootstrap ClassLoader(引导类加载器)

    • 作用:负责加载 JVM 自带的核心类库,比如 java.lang.*java.util.*java.io.* 等。这些类通常位于 JAVA_HOME/lib 目录下的 rt.jar 文件中。
    • 语言实现:Bootstrap ClassLoader 不是由 Java 编写的,它是用本地代码实现的(C/C++),直接集成在 JVM 中。
    • 特点:由于它是 JVM 自带的,所以开发者无法直接获取或操作它,且其加载的类位于 Java 的核心类库之中。
  2. Extension ClassLoader(扩展类加载器)

    • 作用:负责加载扩展类库,通常位于 JAVA_HOME/lib/ext 目录中的 JAR 包。这些类库为标准的 Java 类库提供扩展功能。
    • 实现:由 Java 实现,继承自 ClassLoader 类。
    • 特点:可以通过 ClassLoader.getParent() 方法访问到。
  3. Application ClassLoader(应用程序类加载器)

    • 作用:负责加载应用程序的类文件,通常加载我们编写的代码(classpath 中指定的类)。它加载的类通常位于项目的 classpath 路径下。
    • 实现:由 Java 实现,也是我们日常使用的类加载器。通常通过 Thread.currentThread().getContextClassLoader() 获取。
    • 特点:可以由开发者直接操作和管理。

双亲委派模型

双亲委派模型规定:类加载器在加载类时,先将任务委派给它的父类加载器,直到 Bootstrap ClassLoader 为止。只有当父类加载器无法找到指定的类时,子类加载器才会尝试自己加载。这种机制确保了 Java 核心类库的安全性和唯一性。

加载过程示意:

  1. Application ClassLoader 要加载一个类时,首先把请求委托给它的父类 Extension ClassLoader
  2. Extension ClassLoader 会再把请求委托给 Bootstrap ClassLoader
  3. Bootstrap ClassLoader 尝试从核心类库中加载类,如果成功则返回,否则把控制权交回给 Extension ClassLoader
  4. 如果 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 及之前版本)等文件中加载类。
  • 不继承 ClassLoaderBootstrap 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 核心类库的安全和稳定性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值