Java 魔法类 Unsafe 源码解读(一)

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 的操作主要可以分为以下几类:

  1. 内存操作
  2. 内存屏障
  3. 对象操作
  4. 数据操作
  5. CAS 操作
  6. 线程调度
  7. Class 调度
  8. 系统信息

内存操作

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);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值