Unsafe 类进阶:分析如何绕过 JVM 限制直接读写内存、挂起线程以及实现 CAS 操作

酷雪大大_6730

酷雪大大_6730

2026-06-02

747人浏览

原创

unsafe是jvm底层机制的公开出口,提供直接内存操作、park/unpark线程调度、cas原子指令及绕过构造器等能力,支撑netty零拷贝、aqs锁等高性能组件,但误用会导致jvm崩溃或数据损坏。

unsafe 类进阶:分析如何绕过 jvm 限制直接读写内存、挂起线程以及实现 cas 操作

Unsafe 类不是用来“绕过限制”的玩具,而是 JVM 底层机制的公开出口。它不破坏规则,而是暴露了规则之下真实运行的那层——内存地址、CPU 原子指令、线程调度原语。用得好,是 Netty 零拷贝、AQS 锁、AtomicInteger 的基石;用错一步,可能直接触发段错误或静默数据损坏。

直接读写任意内存地址

Unsafe 提供了 putXXX 和 getXxx 系列方法(如 putInt、getInt、putObject、getObject),但它们操作的不是 Java 对象字段,而是**裸内存地址**。关键前提是:你得先拿到一个合法的 long 类型地址值。

  • 地址通常来自 allocateMemory 分配的堆外内存,例如:long addr = unsafe.allocateMemory(4);
  • 也可通过 objectFieldOffset 获取对象字段在内存中的偏移量,再结合对象基址(staticFieldBase 或 arrayBaseOffset)算出绝对地址
  • 写入时无需类型检查或访问控制——unsafe.putInt(addr, 123) 直接覆写 4 字节;读取同理,unsafe.getInt(addr) 按 int 解析该地址内容
  • 风险在于:地址无效会 crash JVM;类型误判(比如把 double 地址当 int 读)导致值错乱;无 GC 管理,必须手动 freeMemory

挂起与唤醒线程(park/unpark)

Unsafe 的 park 和 unpark 是 JVM 线程阻塞机制的底层实现,比 Object.wait/notify 更轻量、更可控,且不依赖 synchronized 块。

求职精灵
求职精灵

一款AI办公效率工具,主要用于有见求职旗下AI求职平台,提供AI简历优化、AI模拟面试、岗位查询、职业规划等,适合需要提升相关任务效率的用户。

下载
  • unsafe.park(false, 0) 让当前线程无限期挂起,直到被其他线程 unpark
  • unsafe.unpark(thread) 唤醒指定线程;若线程尚未 park,则下次 park 会立即返回(即支持“许可预发”)
  • AQS 就靠这对方法实现等待队列的线程调度:获取锁失败 → park;释放锁并唤醒后继 → unpark
  • 注意:park 不释放任何锁,也不参与 monitor 机制;它纯粹是线程状态切换,和操作系统 futex 或 Windows Event 一一对应

CAS 操作:无锁并发的硬件级保障

CAS 不是 Java 层逻辑,而是 Unsafe 将 CPU 的 cmpxchg(x86)或 ldaxr/stlxr(ARM)等原子指令封装成的 Java 方法。它保证“读-判-写”三步不可分割。

  • 典型调用:unsafe.compareAndSwapInt(obj, offset, expected, newValue)
  • obj 是目标对象,offset 是字段在对象内存布局中的字节偏移(非 public 字段也能改),expected 是期望旧值,newValue 是待写入值
  • 成功返回 true,失败返回 false —— 不抛异常,由上层决定重试、降级或放弃
  • ABA 问题真实存在:某字段从 A→B→A,CAS 会误判为“未变”。解决方式不是避免 CAS,而是引入版本号(如 AtomicStampedReference)
  • 所有 java.util.concurrent.atomic 包下的类,底层都走这一套;没有 Unsafe,就没有真正的无锁结构

绕过构造器与字段访问限制

Unsafe 允许跳过语言层约束,直击运行时本质:

  • allocateInstance(Class) 创建对象实例,完全不调用任何构造方法(包括 private 构造器),字段全为默认值(0/null/false)
  • putXxx 可修改 final 字段——不是“反射破 final”,而是直接写内存,JVM 不做 final 语义校验
  • staticFieldOffset + staticFieldBase 可定位静态字段地址,配合 put/get 实现静态变量原子更新(比 volatile 更底层)
  • 这些能力被序列化框架(Kryo)、RPC 库(gRPC Java 版反序列化)、缓存系统(Caffeine 内部对象重建)所依赖,但业务代码几乎不该触碰

相关专题

更多
C语言变量命名
C语言变量命名

c语言变量名规则是:1、变量名以英文字母开头;2、变量名中的字母是区分大小写的;3、变量名不能是关键字;4、变量名中不能包含空格、标点符号和类型说明符。php中文网还提供c语言变量的相关下载、相关课程等内容,供大家免费下载使用。

2023.06.20

2989

3

c语言入门自学零基础
c语言入门自学零基础

C语言是当代人学习及生活中的必备基础知识,应用十分广泛,本专题为大家c语言入门自学零基础的相关文章,以及相关课程,感兴趣的朋友千万不要错过了。

2023.07.25

2228

9

c语言运算符的优先级顺序
c语言运算符的优先级顺序

c语言运算符的优先级顺序是括号运算符 > 一元运算符 > 算术运算符 > 移位运算符 > 关系运算符 > 位运算符 > 逻辑运算符 > 赋值运算符 > 逗号运算符。本专题为大家提供c语言运算符相关的各种文章、以及下载和课程。

2023.08.02

1200

5

c语言数据结构
c语言数据结构

数据结构是指将数据按照一定的方式组织和存储的方法。它是计算机科学中的重要概念,用来描述和解决实际问题中的数据组织和处理问题。数据结构可以分为线性结构和非线性结构。线性结构包括数组、链表、堆栈和队列等,而非线性结构包括树和图等。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.09

1138

4

c语言random函数用法
c语言random函数用法

c语言random函数用法:1、random.random,随机生成(0,1)之间的浮点数;2、random.randint,随机生成在范围之内的整数,两个参数分别表示上限和下限;3、random.randrange,在指定范围内,按指定基数递增的集合中获得一个随机数;4、random.choice,从序列中随机抽选一个数;5、random.shuffle,随机排序。

2023.09.05

1336

5

c语言const用法
c语言const用法

const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

2023.09.20

2078

7

c语言get函数的用法
c语言get函数的用法

get函数是一个用于从输入流中获取字符的函数。可以从键盘、文件或其他输入设备中读取字符,并将其存储在指定的变量中。本文介绍了get函数的用法以及一些相关的注意事项。希望这篇文章能够帮助你更好地理解和使用get函数 。

2023.09.20

3280

8

c数组初始化的方法
c数组初始化的方法

c语言数组初始化的方法有直接赋值法、不完全初始化法、省略数组长度法和二维数组初始化法。详细介绍:1、直接赋值法,这种方法可以直接将数组的值进行初始化;2、不完全初始化法,。这种方法可以在一定程度上节省内存空间;3、省略数组长度法,这种方法可以让编译器自动计算数组的长度;4、二维数组初始化法等等。

2023.09.22

14695

6

c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

2023.09.22

549

3

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.6万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 133.4万人学习