sun.misc.Unsafe中一些常用方法记录
sun.misc.Unsafe中一些常用方法记录
前情摘要
sun公司提供了可以用于直接操作内存的类,这个类就是sun.misc.Unsafe
。因为Java本身是不会涉及到直接操作内存的,Java API也没有提供这些操作,内存管理全部交给虚拟机来做。Sun之所以提供这个类,因为有些功能现有的Java API满足不了,如果没有这个类,可能就没有现在原子类,J.U.C包了,也许也没了各种Concurrent Collection类,可能也没了NIO的堆外内存,所以这个类十分的有用,并且很重要,Sun也没有开放这个类的源代码,并且对它的使用也做了一些限制。
通过反编译看到这个类中,几乎所有的方法都是native修饰的,即使其他非native修饰的,最后也是在调用本类的其他native方法。
Unsafe提供了一个static修饰的静态方法,用来获取这个类的实例,这是一个单例。
@CallerSensitive
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}
这个getUnsafe()方法并不是直接返回Unsafe的实例,而是做了校验。通过校验直接使用getUnsafe这个方法的类的类加载器是否为Bootstrap类加载器,来做安全检查。由于Java API中的类都是由Bootstrap类加载器加载的,所以是可以直接调用这个方法来获取Unsafe的实例。不过如果要在自己的代码中使用Unsafe,则需要通过反射修改theUnsafe
字段的访问修饰符,然后获取Unsafe的实例。由于Unsafe可以直接操作内存,所以存在很大的风险,使用时需要特别谨慎!
想阅读下关于原子类和J.U.C包下的一些实现,发现这些经常使用的方法,记录下每个方法的具体含义,不然没法理解整个客户端方法的行为。
整个Unsafe类中共分为以下几类操作:
- 底层内存信息相关的 比如内存页大小
- 操作对象及其字段 如:实例化对象,获取实例域的偏移量
- 操作类以及静态字段 如:定义一个类,获取静态字段的偏移量
- 操作数据对象
- 同步操作 提供操作监控器和CAS的支持
- 内存操作 主要是内存的分配,复制,销毁等 NIO中用到的多
JUC和原子类以及并发集合中主要用到以下这些方法
Unsafe.putObject()
将一个引用类型的值存入到给定对象的变量中,这个方法的参数列表为putObject(Object o, long offset, Object x)
,这里就是将对象x的引用,存到对象o的偏移量为offset的变量上。要注意这个操作不保证内存可见性,也就是说对对象o的指定字段的更新,并不会在多线程环境下被其他线程发现值的变动。所以提供了一个具有volatile语义的方法putObjectVolatile()
,这个操作具有volatile语义的store语义,并不具备load的语义,如果想在获取时具备load的语义,可以使用getObjectVolatile()
。
Unsafe.park()
此方法主要用于阻塞当前线程,方法的参数为park(boolean absolute, long time)
,如果absolute为false,time为0,则表示一直阻塞,直至unpark方法被调用,或者被中断。如果absolute为false,time不为0,则表示为给定的纳秒过后,中断阻塞,相应的线程可以被系统调度。如果absolute为true,time的单位为毫秒,不过这是一个绝对时间,Epoch Time—— Unix纪元时间 1970.1.1 零时,absolute表示的绝对是指基于这个时间的绝对时间,也就是说park(true, System.currentTimeMillis()+N)
这种写法才是对的,不然输入的任意数字都没有作用的,不会起到阻塞线程的效果。
会将线程一直阻塞,直至以下情况发生:
- 当相应的unpark方法在park方法调用前被调用,park方法调用会被立即返回
- 当相应的unpark方法在park方法后被调用,park方法返回
- 在调用park方法的前后,如果检测到线程已经被设置为中断,则park方法立即返回
- absolute为false并且time不为0,所给的纳秒已经过了
- absolute为true,并且所给的时间(必须是在Epoch纪元时间的基础上,通常取当前距离纪元时间的毫秒数加上希望阻塞时间毫秒数)毫秒已经用完
- 无理由返回
Unsafe.unpark()
将指定的线程从park阻塞状态中恢复过来,方法仅有一个参数unpark(Thraed thread)
,要确保thread对象没有被销毁,也就是要检查不为null
Unsafe.getObjectVolatile()
获取所给对象的所给变量的值,使用volatile语义的load语义,会在实际获取这个值的时候从主存中加载,不会使用CPU缓存中的,总能确保获取到的是有效的值。 getObjectVolatile(Object o, long offset)
Unsafe.getInt()
有三个重载方法getInt(Object o, long offset)
、getInt(long address)
和getIntVolatile(long address)
,都是从指定的位置获取变量的值,只不过第一个的offset是相对于对象o的相对偏移量,第二个address是绝对地址偏移量。如果第一个方法中o为null是,offset也会被作为绝对偏移量。第三个则是带有volatile语义的load读操作。
Unsafe.putInt()
同样有三个在用的重载方法,参数也差不多同putInt
类似,含义则是相反,用于对指定内存地址的变量赋值。
putInt(Object o, long offset, int x)
、putInt(long address)
和putIntVolatile(Object o, long offset, int x)
,最后一个是Volatile版本的putInt方法。
Unsafe.objectFieldOffset()
这个方法的作用是用来获取指定对象的域的偏移量,这个是指这个字段在内存中的位置相对于这个对象在内存中的起始地址的偏移量,也就是隔了多远,有了这个值,后续就能直接定位到这个域的内存地址,然后获取其中的值去操作。注意,这个方法只适用于域为非static修饰的,static修饰的域需要使用Unsafe.staticFieldOffset()
Usage:
long offset = unsafe.objectFieldOffset(Field var)
方法结收一个java.lang.reflect.Field对象。
静态域的偏移量也是一样的使用,方法名不同。
Unsafe.compareAndSwapObject()
以CAS的方式来更新一个引用类型的字段值,如果更新成功则返回true,否则返回false。
Usage:
boolean casResult = unsafe.compareAndSwapObject(object,offset,expected,update);
这个方法本身不会有自旋行为,直接做CAS操作,如果失败立即返回。通常是我们在代码中通过无限循环实现CAS的自旋,并且需要被更新的域一般都是用volatile
修饰的,不然多线程环境下无法保证正确性。
如果想通过此方式来CAS操作静态域的值,第一个参数为静态域所在的Class对象。
Unsafe.compareAndSwapInt()
和上面的方法作用一样,只不过是用来操作int类型的变量
Unsafe.putOrderedInt()
Unsafe.putIntVolatile()
的有序版本/延迟版本,只保证最终将制定的变量更新为新的值。
Unsafe.getAndSetInt()
这个不属于native方法,是用getIntVolatile
和compareAndSwapInt
两个方法组合,对指定的变量设置为新的值,不过会返回设置新值前的旧值,而不是无条件直接设置,在竞争的条件下,这样就需要在循环中不断做CAS。
Unsafe.getAndAddInt()
这个同样不属于native方法,和上面差不多,只不过是在旧值的基础上做了个加新值的操作
sun.misc.Unsafe中一些常用方法记录的更多相关文章
- JDK 1.8 sun.misc.Unsafe类CAS底层实现
在java.util.concurrent包下面的很多类为了追求性能都采用了sun.misc.Unsafe类中的CAS操作,从而避免使用synchronized等加锁方式带来性能上的不足. 在sun. ...
- Java中的sun.misc.Unsafe包
chronicle项目:https://github.com/peter-lawrey/Java-Chronicle 这个项目是利用mmap机制来实现高效的读写数据,号称每秒写入5到20百万条数据. ...
- Java sun.misc.Unsafe类的学习笔记
Java未开源的Unsafe类 Unsafe类可以为我们提供高效并且线程安全方式操作变量,直接和内存数据打交道. 获取Unsafe实体的方法 private static Unsafe getUnsa ...
- 一文了解sun.misc.Unsafe
Java语言和JVM平台已经度过了20岁的生日.它最初起源于机顶盒.移动设备和Java-Card,同时也应用在了各种服务器系统中,Java已成为物联网(Internet of Things)的通用语言 ...
- 并发编程之sun.misc.Unsafe类
1.Unsafe知识点整理 2.代码: package com.javabasic.unsafe; import java.lang.reflect.Field; import sun.misc.Un ...
- sun.misc.Unsafe的理解
以下sun.misc.Unsafe源码和demo基于jdk1.7: 最近在看J.U.C里的源码,很多都用到了sun.misc.Unsafe这个类,一知半解,看起来总感觉有点不尽兴,所以打算对Unsaf ...
- java对象的内存布局(二):利用sun.misc.Unsafe获取类字段的偏移地址和读取字段的值
在上一篇文章中.我们列出了计算java对象大小的几个结论以及jol工具的使用,jol工具的源代码有兴趣的能够去看下.如今我们利用JDK中的sun.misc.Unsafe来计算下字段的偏移地址,一则验证 ...
- sun.misc.Unsafe 详解
原文地址 译者:许巧辉 校对:梁海舰 Java是一门安全的编程语言,防止程序员犯很多愚蠢的错误,它们大部分是基于内存管理的.但是,有一种方式可以有意的执行一些不安全.容易犯错的操作,那就是使用Unsa ...
- Java sun.misc.unsafe类
Java是一个安全的开发工具,它阻止开发人员犯很多低级的错误,而大部份的错误都是基于内存管理方面的.如果你想搞破坏,可以使用Unsafe这个类.这个类是属于sun.*API中的类,并且它不是J2SE中 ...
随机推荐
- TensorFlow——常见张量操作的API函数
1.张量 张量可以说是TensorFlow的标志,因为整个框架的名称TensorFlow就是张量流的意思,全面的认识一下张量.在TensorFlow程序使用tensor数据结构来代表所有的数据,在计算 ...
- 网络流入门题目 - bzoj 1001
现在小朋友们最喜欢的"喜羊羊与灰太狼",话说灰太狼抓羊不到,但抓兔子还是比较在行的, 而且现在的兔子还比较笨,它们只有两个窝,现在你做为狼王,面对下面这样一个网格的地形: 左上角点 ...
- 关于neo4j初入门(1)
图形数据库也称为图形数据库管理系统或GDBMS. Neo4j的官方网站:http://www.neo4j.org Neo4j的优点 它很容易表示连接的数据 检索/遍历/导航更多的连接数据是非常容易和快 ...
- Flask DBUtils
作用:创建连接池,解决多线程问题 1.安装模块 pip3 install -i https://pypi.douban.com/simple DBUtils 2.settings.py(配置文件) f ...
- git 远程托管
1.创建别名 git remote add orgin(别名) url 2.推入云端 git push 别名 master(分支) git push 别名 dev 3.克隆(默认只有master分支) ...
- Qt Installer Framework翻译(3-4)
更新组件 下图说明了用于更新已安装组件的默认工作流程: 本节使用在macOS上运行的Qt 5维护工具为例,来演示用户如何更新已安装组件. 启动更新程序 用户启动维护工具时,将打开"简介&qu ...
- spring boot 整合 swagger2
swagger2为了更好的管理api文档接口 swagger构建的api文档如下,清晰,避免了手写api诸多痛点 一,添加依赖 <!--swagger2的官方依赖--> <depen ...
- ssm之spring+springmvc+mybatis整合初探
1.基本目录如下 2.首先是向lib中加入相应的jar包 3.然后在web.xml中加入配置,使spring和springmvc配置文件起作用. <?xml version="1. ...
- Oracle GoldenGate for DB2
--Enable logdb2 update db cfg using LOGARCHMETH1 DISK:/home/db2inst1/arclogs--Rebootdb2 terminatedb2 ...
- Pandas中merge和join的区别
可以说merge包含了join的操作,merge支持通过列或索引连表,而join只支持通过索引连表,只是简化了merge的索引连表的参数 示例 定义一个left的DataFrame left=pd.D ...