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方法,是用getIntVolatilecompareAndSwapInt两个方法组合,对指定的变量设置为新的值,不过会返回设置新值前的旧值,而不是无条件直接设置,在竞争的条件下,这样就需要在循环中不断做CAS。

Unsafe.getAndAddInt()

这个同样不属于native方法,和上面差不多,只不过是在旧值的基础上做了个加新值的操作

原文地址:https://www.cnblogs.com/heartlake/p/12353287.html

时间: 2024-10-08 12:29:39

sun.misc.Unsafe中一些常用方法记录的相关文章

认识 sun.misc.Unsafe

笼罩在迷雾之中的 Unsafe 私有API,有人认为应该废弃,也有人认为应该开放. [2015年07月28日] Oracle 宣称要在 Java 9 中去除私有 API: sun.misc.Unsafe, 这就像点燃了炸药桶, 遭到 许多开发者的抗议, 他们认为 这会严重破坏Java的生态系统 开源博主 Rafael Winterhalter 在博文 "Understanding sun.misc.Unsafe" 中说, 底层编程(low-level programming) 中经常会

Java中的sun.misc.Unsafe包

chronicle项目:https://github.com/peter-lawrey/Java-Chronicle 这个项目是利用mmap机制来实现高效的读写数据,号称每秒写入5到20百万条数据. 作者有个测试,写入1百万条log用时0.234秒,用java自带的logger,用时7.347秒. 在看chronicle的源代码,发现一个牛B的利用Unsafe来直接读写内存,从而提高效率的例子. 详细见这个类:https://github.com/peter-lawrey/Java-Chroni

使用sun.misc.Unsafe及反射对内存进行内省(introspection)

对于一个有经验的JAVA程序员来说,了解一个或者其它的JAVA对象占用了多少内存,这将会非常有用.你可能已经听说过我们所生活的世界,存储容量将不再是一个问题,这个对于你的文本编辑器来说可能是对的(不过,打开一个包含大量的图片以及图表的文档,看看你的编辑器会消耗多少内存),对于一个专用服务器软件来说也可能是对的(至少在你的企业成长到足够大或者是在同一台服务器运行其它的软件之前),对于基于云的软件来说也可能是对的,如果你足够的富有可以花足够的钱可以买顶级的服务器硬件. 然而,现实是你的软件如果是受到

一文了解sun.misc.Unsafe

Java语言和JVM平台已经度过了20岁的生日.它最初起源于机顶盒.移动设备和Java-Card,同时也应用在了各种服务器系统中,Java已成为物联网(Internet of Things)的通用语言.我们显然可以看到Java已经无处不在! 但是不那么为人所知的是,Java也广泛应用于各种低延迟的应用中,如游戏服务器和高频率的交易应用.这只所以能够实现要归功于Java的类和包在可见性规则中有一个恰到好处的漏洞,让我们能够使用一个很便利的类,这个类就是sun.misc.Unsafe.这个类从过去到

Java Magic. Part 4: sun.misc.Unsafe

原文地址 译文地址 译者:许巧辉 校对:梁海舰 Java是一门安全的编程语言,防止程序员犯很多愚蠢的错误,它们大部分是基于内存管理的.但是,有一种方式可以有意的执行一些不安全.容易犯错的操作,那就是使用Unsafe类. 本文是sun.misc.Unsafe公共API的简要概述,及其一些有趣的用法. Unsafe 实例 在使用Unsafe之前,我们需要创建Unsafe对象的实例.这并不像Unsafe unsafe = new Unsafe()这么简单,因为Unsafe的构造器是私有的.它也有一个静

Java sun.misc.unsafe类的使用

Java是一个安全的开发工具,它阻止开发人员犯很多低级的错误,而大部份的错误都是基于内存管理方面的.如果你想搞破坏,可以使用Unsafe这个类.这个类是属于sun.*API中的类,并且它不是J2SE中真正的一部份,因此你可能找不到任何的官方文档,更可悲的是,它也没有比较好的代码文档. 1.实例化sun.misc.Unsafe 如果你尝试创建Unsafe类的实例,基于以下两种原因是不被允许的. 1).Unsafe类的构造函数是私有的: 2).虽然它有静态的getUnsafe()方法,但是如果你尝试

sun.misc.unsafe

Java中大部分错误都是基于内存管理方面的.如果想破坏,可以使用Unsafe这个类. 实例化Unsafe: 下面两种方式是不行的 private Unsafe() {} //私有构造方法 @CallerSensitive public static Unsafe getUnsafe() { Class var0 = Reflection.getCallerClass(); if(!VM.isSystemDomainLoader(var0.getClassLoader())) {//如果不是JDK

sun.misc.unsafe类的使用

这个帖子是关于JAVA中鲜为人知的特性的后续更新,如果想得到下次在线讨论的更新,请通过邮件订阅,并且不要忘了在评论区留下你的意见和建议. Java是一个安全的开发工具,它阻止开发人员犯很多低级的错误,而大部份的错误都是基于内存管理方面的.如果你想搞破坏,可以使用Unsafe这个类.这个类是属于sun.* API中的类,并且它不是J2SE中真正的一部份,因此你可能找不到任何的官方文档,更可悲的是,它也没有比较好的代码文档. 实例化sun.misc.Unsafe 如果你尝试创建Unsafe类的实例,

java对象的内存布局(二):利用sun.misc.Unsafe获取类字段的偏移地址和读取字段的值

在上一篇文章中.我们列出了计算java对象大小的几个结论以及jol工具的使用,jol工具的源代码有兴趣的能够去看下.如今我们利用JDK中的sun.misc.Unsafe来计算下字段的偏移地址,一则验证下之前文章中的结论,再则跟jol输出结果对照下.怎样获取sun.misc.Unsafe对象.能够參考这篇文章. public class VO { public int a = 0; public long b = 0; public static String c= "123"; pub