专栏名称: 鸭哥聊Java
回复关键字:666 ,领取免费简历模板,Java面试题,Java编程视频等。本号内容涵盖Java源码,JVM源码,Dubbo源码,Spring源码,Spring Cloud微服务架构,分布式高并发架构技术,MySQL性能调优等。
目录
相关文章推荐
编程茶座  ·  这心态真是绝了! ·  昨天  
川大研究生  ·  乐活 | ... ·  2 天前  
川大研究生  ·  乐活 | ... ·  2 天前  
高分子科学前沿  ·  刘军教授、支春义教授、吕海明教授Angew: ... ·  2 天前  
高分子科学前沿  ·  中山大学吴丁财、朱有龙团队《AM》:芳香胺连 ... ·  3 天前  
艾邦高分子  ·  30+(国内)气凝胶材料生产企业盘点 ·  3 天前  
51好读  ›  专栏  ›  鸭哥聊Java

话说,你们会毫无保留得把技术分享给同事吗?

鸭哥聊Java  · 公众号  ·  · 2025-02-23 10:19

正文

看到一个帖子讨论是否要毫无保留地将技术分享给同事,里面的网友说“能分享的技术都不是技术”,我觉得这话有点道理但也太偏激了。

确实,技术的核心不只是代码本身,更重要的是背后的思维和解决问题的能力。如果你能独立探索和掌握这些技术,那是别人学不来的。所以,分享一些常见的技术和工具没有太大问题,甚至能帮助自己在团队中树立技术权威。

但如果涉及到核心业务代码和公司独有的解决方案,那就得谨慎了。这些东西可能涉及到公司利益,也有可能是你工作中的“竞争力”。我认为,适度分享技术知识是好的,但核心内容还是应该自己把控,避免被不懂的人“拿去”再超越。

技术分享是双向的,不光是为了别人,更是为了自己。保护好核心内容的同时,也可以让技术在团队中流动和积累 【备注:文末可领最新资料】

今日面试题


好了,我们回归正题, 今天我们聊一个稍微有点危险,但又相当有趣的话题—— Unsafe 类。 看过 JUC(Java 并发工具包)源码的同学应该不陌生,这个名字频繁出现,甚至很多并发工具类都在用它。

那究竟什么是 Unsafe ,它又是怎么在底层帮助我们提高性能的呢? 别急,往下看,我带你一步步了解。

什么是 Unsafe?

首先, Unsafe 是 Java 中一个非常底层的类,属于 sun.misc 包。它提供了一些相当低级的操作能力,类似于 C 语言中的指针,可以直接访问和操作系统内存。这种能力,放在 Java 里就像是打开了一个 Pandora's Box,既有超强的力量,也充满了危险。

为什么要使用 Unsafe

Java 是一门安全性很高的语言,JVM 的垃圾回收机制、内存管理、线程安全等方面都做得相当不错。然而,在某些极端情况下(例如对性能的要求特别高时),我们可能需要越过这些安全壁垒,直接对内存进行控制,这时候 Unsafe 就派上用场了。

它提供的这些方法通常会涉及直接内存访问、内存管理、甚至是对象的直接操作。比如,它允许我们直接分配和释放内存,进行内存拷贝,甚至修改内存的某些字节值。

换句话说, Unsafe 可以让你像 C 语言一样做内存操作,但它也意味着你有可能一不小心就把内存给弄乱了,导致程序崩溃或产生难以追踪的错误。

Unsafe 的创建

获取 Unsafe 实例

如果你尝试直接调用 Unsafe.getUnsafe() 来获取这个类的实例,绝对会碰壁。因为该方法会做一系列的安全检查,要求调用者的 classLoader 必须是引导类加载器(Bootstrap ClassLoader)加载的类。如果不是,就会抛出 SecurityException

这是什么鬼?我们都知道 Unsafe 的功能相当强大,不想让它随便被不可信代码使用,所以 Java 通过这个安全检查来限制对它的访问。

这是为了防止一些不受信任的代码使用 Unsafe 时,搞乱程序的内存操作,甚至带来安全隐患。

不过,不怕。我们可以通过反射来绕过这个安全检查,获取 Unsafe 的实例。代码如下:

private static Unsafe reflectGetUnsafe() {
    try {
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        return (Unsafe) field.get(null);
    } catch (Exception e) {
        log.error(e.getMessage(), e);
        return null;
    }
}

这个方法通过反射获取 Unsafe 类中已经创建的单例对象 theUnsafe ,绕过了安全机制。

另外,还可以通过 -Xbootclasspath/a 命令行参数,将包含 Unsafe 调用的类所在的 jar 包路径添加到默认的引导类加载路径中,进而绕过安全限制。

java -Xbootclasspath/a: ${path}   // ${path} 为包含 Unsafe 调用类的 jar 包路径

Unsafe 的功能

虽然 Unsafe 的功能很多,但总的来说,它提供了以下几类操作:

  1. 内存操作 :分配、释放、修改内存内容等。
  2. 内存屏障 :控制 CPU 指令的执行顺序,确保内存操作的有序性。
  3. 对象操作 :直接操作对象的字段。
  4. 数据操作 :直接读取和写入数据。
  5. CAS 操作 :提供原子操作,确保多线程环境中的数据一致性。
  6. 线程调度 :调度线程的执行。
  7. Class 操作 :动态生成类并操作它们。
  8. 系统信息 :访问操作系统的底层信息。

接下来,我们主要看一下 Unsafe 在内存操作方面的几个常用方法。

内存操作

对于程序员来说,内存的直接操作并不陌生。在 C/C++ 中,我们可以通过指针来直接访问内存,这在 Java 中是不允许的,因为 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);

举个简单的例子,假设我们想分配一块内存,向它写入数据,再把数据拷贝到另一块内存中,代码可以这么写:

private void memoryTest() {
    int size = 4;
    long addr = unsafe.allocateMemory(size);
    long addr3 = unsafe.reallocateMemory(addr, size * 2);
    System.out.println("addr: "+addr);
    System.out.println("addr3: "+addr3);
    try {
        unsafe.setMemory(null, addr, size, (byte1);
        for (int i = 0; i 2; i++) {
            unsafe.copyMemory(null, addr, null, addr3 + size * i, 4);
        }
        System.out.println(unsafe.getInt(addr));
        System.out.println(unsafe.getLong(addr3));
    } finally {
        unsafe.freeMemory(addr);
        unsafe.freeMemory(addr3);
    }
}

输出结果:

addr: 2433733895744
addr3: 2433733894944
16843009
72340172838076673

结果分析

首先,我们使用 allocateMemory 方法分配了 4 字节的内存空间,并通过 setMemory 方法填充内存,接着使用 copyMemory 将内存拷贝到了另一块内存区域。最后,通过 getInt getLong 方法读取内存中的内容。

比如,输出的 16843009 就是由于我们写入的 4 个字节数据拼接成了一个整数。详细的字节内容和转换过程,可以参考下图:







请到「今天看啥」查看全文