伍佰目录 短网址
  当前位置:海洋目录网 » 站长资讯 » 站长资讯 » 文章详细 订阅RssFeed

Unsafe 的 CAS 和内存操作的原理、源码解毒

来源:本站原创 浏览:90次 时间:2022-06-05

第一时间获取技术干货和业界资讯!

Java 语言的一大特点就是跨平台,并且提供的有一套完美的内存管理机制。但这都是 JVM 提供的,如果我们想要直接访问系统内存资源、自主管理内存资源等就无法实现。于是 Java 又提供了一个魔法类:Unsafe。

Unsafe 类位于 sun.misc 包中。从名字看,这个类就是一个不安全的类,实际上它确实是封装了一些不安全的操作!

Unsafe 类和 String 类一样的被定义为 final,也就是说它不可以被继承。并且 Unsafe 被设计成了单例,构造函数是私有的,只能通过 getUnsafe 方法获得它。除此之外,getUnsafe 方法还设置了限制条件,只有授信的代码才能获得该类的实例。哪些是授信的代码呢?当然是 JDK 库里面的类是可以随意使用的。

说了半天,这个类,我们无法使用,你讲它又何意义?

别急,Java 虽然不建议我们使用它,但是我们还是可以通过两种方式来使用它。

第一种方式是:让我们的代码在启动时“授信”。在运行程序时,指定 bootclasspath 选项,让你使用 Unsafe 实例的类被引导类加载器加载,从而通过 Unsafe.getUnsafe 方法安全的获取 Unsafe 实例。

这个做法比较少用,所以推荐大家采用第二种方法:通过反射来使用它。

注意有的 IDE 可能支持的不是很友好。比如:eclipse 显示”Access restriction…”错误,但如果你运行代码,它将正常运行。如果这个错误提示令人烦恼,可以通过以下设置来避免:

Unsafe 有 8 大功能,很多号主只讲了它的 CAS 功能。


如上图所示,Unsafe 提供的 105 个 API 大致可分为内存操作、CAS、Class 相关、对象操作、线程调度、系统信息获取、内存屏障、数组操作等。今天我先来说两个大功能:CAS 和内存操作(和我前面的《手把手教你通过Java代码体验强引用、软引用、弱引用、虚引用的区别》、《90%的程序员可能都不了解的堆外内存》都有些关联,这是一个系列)。

CAS 操作主要涉及到下面 3 个 API。

CAS 即比较并替换,实现并发算法时常用到的一种技术。CAS 操作包含三个操作数——内存位置、预期原值及新值。执行 CAS 操作的时候,将内存位置的值与预期原值比较,如果相匹配,那么处理器会自动将该位置值更新为新值,否则,处理器不做任何操作。我们都知道,CAS 是一条 CPU 的原子指令(cmpxchg 指令),不会造成所谓的数据不一致问题,Unsafe 提供的 CAS 方法(如 compareAndSwapXXX)底层实现即为 CPU 指令 cmpxchg。

CAS 在 java.util.concurrent.atomic 相关类、Java AQS、CurrentHashMap 等实现上有非常广泛的应用。比如,在 AtomicInteger 的实现中,静态字段 valueOffset 即为字段 value 的内存偏移地址,valueOffset 的值在 AtomicInteger 初始化时,在静态代码块中通过 Unsafe 的 objectFieldOffset 方法获取。在 AtomicInteger 中提供的线程安全方法中,通过字段 valueOffset 的值可以定位到 AtomicInteger 对象中 value 的内存地址,从而可以根据 CAS 实现对 value 字段的原子操作。


比如,下图就为某个 AtomicInteger 对象自增操作前后的内存示意图,对象的基地址 baseAddress=“0x110000”,通过 baseAddress+valueOffset 得到 value 的内存地址 valueAddress=“0x11000c”;然后通过 CAS 进行原子性的更新操作,成功则返回,否则继续重试,直到更新成功为止。


说完 CAS,我们再来说说 Unsafe 的内存操作。

内存操作主要有下面 9 个 API。

在《手把手教你通过Java代码体验强引用、软引用、弱引用、虚引用的区别》和《90%的程序员可能都不了解的堆外内存》两篇文章中,我已经讲过了。在 Java 中创建的对象都处于堆内内存(heap)中,堆内内存是由 JVM 所管控的 Java 进程内存,并且它们遵循 JVM 的内存管理机制,JVM 会采用垃圾回收机制统一管理堆内存。与之相对的是堆外内存,存在于 JVM 管控之外的内存区域,Java 中对堆外内存的操作,依赖于 Unsafe 提供的操作堆外内存的 native 方法。

使用堆外内存的原因是:

  • 对垃圾回收停顿的改善。由于堆外内存是直接受操作系统管理而不是 JVM,所以当我们使用堆外内存时,即可保持较小的堆内内存规模。从而在 GC 时减少回收停顿对于应用的影响。

  • 提升程序 I/O 操作的性能。通常在 I/O 通信过程中,会存在堆内内存到堆外内存的数据拷贝操作,对于需要频繁进行内存间数据拷贝且生命周期较短的暂存数据,都建议存储到堆外内存。

我前面提到的 DirectByteBuffer,在 Netty、MINA 等 NIO 框架中应用广泛。DirectByteBuffer 对于堆外内存的创建、使用、销毁等逻辑均由 Unsafe 提供的堆外内存 API 来实现。


上图为 DirectByteBuffer 构造函数,创建 DirectByteBuffer 的时候,通过 Unsafe.allocateMemory 分配内存、Unsafe.setMemory 进行内存初始化,而后构建 Cleaner 对象用于跟踪 DirectByteBuffer 对象的垃圾回收,以实现当 DirectByteBuffer 被垃圾回收时,分配的堆外内存一起被释放。具体的释放就是我前面讲的 PhantomReference 虚引用。

以上就是 Unsafe 类 8 大主要功能的 2 个重要的功能。其他功能,我会在对应使用到的框架脑图中串起来讲。当然,如果你们现在希望了解的话,我也可以提前写一下这方便的内容。选择权在于你们的留言和评论!

  推荐站点

  • At-lib分类目录At-lib分类目录

    At-lib网站分类目录汇集全国所有高质量网站,是中国权威的中文网站分类目录,给站长提供免费网址目录提交收录和推荐最新最全的优秀网站大全是名站导航之家

    www.at-lib.cn
  • 中国链接目录中国链接目录

    中国链接目录简称链接目录,是收录优秀网站和淘宝网店的网站分类目录,为您提供优质的网址导航服务,也是网店进行收录推广,站长免费推广网站、加快百度收录、增加友情链接和网站外链的平台。

    www.cnlink.org
  • 35目录网35目录网

    35目录免费收录各类优秀网站,全力打造互动式网站目录,提供网站分类目录检索,关键字搜索功能。欢迎您向35目录推荐、提交优秀网站。

    www.35mulu.com
  • 就要爱网站目录就要爱网站目录

    就要爱网站目录,按主题和类别列出网站。所有提交的网站都经过人工审查,确保质量和无垃圾邮件的结果。

    www.912219.com
  • 伍佰目录伍佰目录

    伍佰网站目录免费收录各类优秀网站,全力打造互动式网站目录,提供网站分类目录检索,关键字搜索功能。欢迎您向伍佰目录推荐、提交优秀网站。

    www.wbwb.net