各大框架都在使用的Unsafe类,到底有多神奇?

前言

几乎每个使用 Java开发的工具、软件基础设施、高性能开发库都在底层使用了sun.misc.Unsafe,比如Netty、Cassandra、Hadoop、Kafka等。

Unsafe类在提升Java运行效率,增强Java语言底层操作能力方面起了很大的作用。但Unsafe类在sun.misc包下,不属于Java标准。

很早之前,在阅读并发编程相关类的源码时,看到Unsafe类,产生了一个疑惑:既然是并发编程中用到的类,为什么命名为Unsafe呢?

深入了解之后才知道,这里的Unsafe并不是说线程安全与否,而是指:该类对于普通的程序员来说是”危险“的,一般应用开发者不会也不应该用到此类。

因为Unsafe类功能过于强大,提供了一些可以绕开JVM的更底层功能。它让Java拥有了像C语言的指针一样操作内存空间的能力,能够提升效率,但也带来了指针的问题。官方并不建议使用,也没提供文档支持,甚至计划在高版本中去掉该类。

但对于开发者来说,了解该类提供的功能更有助于我们学习CAS、并发编程等相关的知识,还是非常有必要学习和了解的。

Unsafe的构造

Unsafe类是"final"的,不允许继承,且构造函数是private,使用了单例模式来通过一个静态方法getUnsafe()来获取。

private Unsafe() {}@CallerSensitivepublic static Unsafe getUnsafe() {Class var0 = Reflection.getCallerClass();if (!VM.isSystemDomainLoader(var0.getClassLoader())) {throw new SecurityException("Unsafe");} else {return theUnsafe;}}

在getUnsafe方法中对单例模式中的对象创建做了限制,如果是普通的调用会抛出一个SecurityException异常。只有由主类加载器加载的类才能调用这个方法。

那么,如何获得Unsafe类的对象呢?通常采用反射机制:

public static Unsafe getUnsafe() throws IllegalAccessException {Field unsafeField = Unsafe.class.getDeclaredFields()[0];unsafeField.setAccessible(true);return (Unsafe) unsafeField.get(null);
}

当获得Unsafe对象之后,就可以”为所欲为“了。下面就来看看,通过Unsafe方法,我们可以做些什么。

Unsafe的主要功能

可先从根据下图从整体上了解一下Unsafe提供的功能:

98efed0cda6c70adb531b0c4a5c51a5d.png
Unsafe功能概述

下面挑选重要的功能进行讲解。

一、内存管理

Unsafe的内存管理功能主要包括:普通读写、volatile读写、有序写入、直接操作内存等分配内存与释放内存的功能。

普通读写

Unsafe可以读写一个类的属性,即便这个属性是私有的,也可以对这个属性进行读写。

// 获取内存地址指向的整数
public native int getInt(Object var1, long var2);// 将整数写入指定内存地址
public native void putInt(Object var1, long var2, int var4);

getInt用于从对象的指定偏移地址处读取一个int。putInt用于在对象指定偏移地址处写入一个int。其他原始类型也提供有对应的方法。

另外,Unsafe的getByte、putByte方法提供了直接在一个地址上进行读写的功能。

volatile读写

普通的读写无法保证可见性和有序性,而volatile读写就可以保证可见性和有序性。

// 获取内存地址指向的整数,并支持volatile语义
public native int getIntVolatile(Object var1, long var2);// 将整数写入指定内存地址,并支持volatile语义
public native void putIntVolatile(Object var1, long var2, int var4);

volatile读写要保证可见性和有序性,相对普通读写更加昂贵。

有序写入

有序写入只保证写入的有序性,不保证可见性,就是说一个线程的写入不保证其他线程立马可见。

// 将整数写入指定内存地址、有序或者有延迟的方法
public native void putOrderedInt(Object var1, long var2, int var4);

而与volatile写入相比putOrderedXX写入代价相对较低,putOrderedXX写入不保证可见性,但是保证有序性,所谓有序性,就是保证指令不会重排序。

直接操作内存

Unsafe提供了直接操作内存的能力:

// 分配内存
public native long allocateMemory(long var1);
// 重新分配内存
public native long reallocateMemory(long var1, long var3);
// 内存初始化
public native void setMemory(long var1, long var3, byte var5);
// 内存复制
public native void copyMemory(Object var1, long var2, Object var4, long var5, long var7);
// 清除内存
public native void freeMemory(long var1);

对应操作内存,也提供了一些获取内存信息的方法:

// 获取内存地址
public native long getAddress(long var1);public native int addressSize();public native int pageSize();

值得注意的是:利用copyMemory方法可以实现一个通用的对象拷贝方法,无需再对每一个对象都实现clone方法,但只能做到对象浅拷贝。

二、非常规对象实例化

通常,我们通过new或反射来实例化对象,而Unsafe类提供的allocateInstance方法,可以直接生成对象实例,且无需调用构造方法和其他初始化方法。

这在对象反序列化的时候会很有用,能够重建和设置final字段,而不需要调用构造方法。

// 直接生成对象实例,不会调用这个实例的构造方法
public native Object allocateInstance(Class<?> var1) throws InstantiationException;

三、类加载

通过以下方法,可以实现类的定义、创建等操作。

// 方法定义一个类,用于动态地创建类
public native Class<?> defineClass(String var1, byte[] var2, int var3, int var4, ClassLoader var5, ProtectionDomain var6);//  动态的创建一个匿名内部类
public native Class<?> defineAnonymousClass(Class<?> var1, byte[] var2, Object[] var3);// 判断是否需要初始化一个类
public native boolean shouldBeInitialized(Class<?> var1);// 保证已经初始化过一个类
public native void ensureClassInitialized(Class<?> var1);

四、偏移量相关

Unsafe提供以下方法获取对象的指针,通过对指针进行偏移,不仅可以直接修改指针指向的数据(即使它们是私有的),甚至可以找到JVM已经认定为垃圾、可以进行回收的对象。

// 获取静态属性Field在对象中的偏移量,读写静态属性时必须获取其偏移量
public native long staticFieldOffset(Field var1);
// 获取非静态属性Field在对象实例中的偏移量,读写对象的非静态属性时会用到这个偏移量
public native long objectFieldOffset(Field var1);
// 返回Field所在的对象
public native Object staticFieldBase(Field var1);
// 返回数组中第一个元素实际地址相对整个数组对象的地址的偏移量
public native int arrayBaseOffset(Class<?> var1);
// 计算数组中第一个元素所占用的内存空间
public native int arrayIndexScale(Class<?> var1);

五、数组操作

数组操作提供了以下方法:

// 获取数组第一个元素的偏移地址
public native int arrayBaseOffset(Class<?> var1);
// 获取数组中元素的增量地址
public native int arrayIndexScale(Class<?> var1);

arrayBaseOffset与arrayIndexScale配合起来使用,就可以定位数组中每个元素在内存中的位置。

由于Java的数组最大值为Integer.MAX_VALUE,使用Unsafe类的内存分配方法可以实现超大数组。实际上这样的数据就可以认为是C数组,因此需要注意在合适的时间释放内存。

六、线程调度

线程调度相关方法如下:

// 唤醒线程
public native void unpark(Object var1);
// 挂起线程
public native void park(boolean var1, long var2);
// 用于加锁,已废弃
public native void monitorEnter(Object var1);
// 用于加锁,已废弃
public native void monitorExit(Object var1);
// 用于加锁,已废弃
public native boolean tryMonitorEnter(Object var1);

通过park方法将线程进行挂起, 线程将一直阻塞到超时或中断条件出现。unpark方法可以终止一个挂起的线程,使其恢复正常。

整个并发框架中对线程的挂起操作被封装在LockSupport类中,LockSupport类中有各种版本pack方法,但最终都调用了Unsafe.park()方法。

七、CAS操作

Unsafe类的CAS操作可能是使用最多的方法。它为Java的锁机制提供了一种新的解决办法,比如AtomicInteger等类都是通过该方法来实现的。compareAndSwap方法是原子的,可以避免繁重的锁机制,提高代码效率。

public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);

CAS一般用于乐观锁,它在Java中有广泛的应用,ConcurrentHashMap,ConcurrentLinkedQueue中都有用到CAS来实现乐观锁。

八、内存屏障

JDK8新引入了用于定义内存屏障、避免代码重排的方法:

// 保证在这个屏障之前的所有读操作都已经完成
public native void loadFence();// 保证在这个屏障之前的所有写操作都已经完成
public native void storeFence();// 保证在这个屏障之前的所有读写操作都已经完成
public native void fullFence();

九、其他

当然,Unsafe类中还提供了大量其他的方法,比如上面提到的CAS操作,以AtomicInteger为例,当我们调用getAndIncrement、getAndDecrement等方法时,本质上调用的就是Unsafe的getAndAddInt方法。

public final int getAndIncrement() {return unsafe.getAndAddInt(this, valueOffset, 1);
}public final int getAndDecrement() {return unsafe.getAndAddInt(this, valueOffset, -1);
}

在实践的过程中,如果阅读其他框架或类库实现,当发现用到Unsafe类,可对照该类的整体功能,结合应用场景进行分析,即可大概了解其功能。

小结

经过本文的分析,想必大家在阅读源码时,再遇到Unsafe类的调用,一定大概猜出它是用来干什么的。使用Unsafe类的主要目的大多数情况下是为了提升运行效率、增强功能。但同时也面临着出错、内存管理等风险。只有深入了解,且有必要的情况下才建议使用。

a9ab3e1e3851ad3ea1c562e9bf614c0d.gif

往期推荐

9d68c4ea64e912521fd269b0af8ee57b.png

synchronized底层是如何实现的?


5a6a2289ff8ac39a6f7e1c7d5a1a33b3.png

线程休眠竟然有 5 种方法?


3eb1a75c62aaa1152a84db48867d4f76.png

虾皮二面:什么是零拷贝?如何实现零拷贝?


fdae7600e60f4cd10b62decaff0bc3e8.gif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/544210.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Codis 分布式缓存部署

为什么80%的码农都做不了架构师&#xff1f;>>> 环境介绍: 1:机器三台 ,IP/hostname 如下, hostname的设置很重要zookeeper / codis的通信都会用到,所以要配置好三台机器的hosts文件. 10.221.8.220 机器的hostname为 Redis1 10.221.8.221 机器的hostname为 Redis…

treeset java_Java TreeSet Higher()方法与示例

treeset javaTreeSet类Higher()方法 (TreeSet Class higher() method) higher() method is available in java.util package. Higher()方法在java.util包中可用。 higher() method is used to return the lowest element in this TreeSet that is higher than the specified el…

怎么解决MySQL死锁问题的?

咱们使用 MySQL 大概率上都会遇到死锁问题&#xff0c;这实在是个令人非常头痛的问题。本文将会对死锁进行相应介绍&#xff0c;对常见的死锁案例进行相关分析与探讨&#xff0c;以及如何去尽可能避免死锁给出一些建议。话不多说&#xff0c;开整&#xff01;什么是死锁死锁是并…

strictmath_Java StrictMath cos()方法与示例

strictmathStrictMath类cos()方法 (StrictMath Class cos() method) cos() method is available in java.lang package. cos()方法在java.lang包中可用。 cos() method is used to return the trigonometric cosine of an angle of the given parameter in the method. Here, c…

Apache cxf JaxRs基本应用

2019独角兽企业重金招聘Python工程师标准>>> 在前一篇中&#xff0c;我们完成了《Apache cxf JaxWs基本应用》 的编写&#xff0c;我们现在实现一个Restful风格的Cxf 。 一、我们首先依旧是基于Maven project配置pom.xml的依赖 [html] view plaincopyprint? <pr…

白嫖1年阿里云,反手就搭一个Java环境

作者 | 磊哥来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;早上收到阿里云小姐姐的消息&#xff0c;阿里云有搞事情了&#xff0c;这次是送一年的阿里云 ECS 服务器。有便宜不占王八蛋…

setseed_Java Random setSeed()方法与示例

setseed随机类setSeed()方法 (Random Class setSeed() method) setSeed() method is available in java.util package. setSeed()方法在java.util包中可用。 setSeed() method is used to set the given seed of this Random Number Generator. setSeed()方法用于设置此随机数生…

.Net 自己写个简单的 半 ORM (练手)

ORM 大家都知道&#xff0c; .Net 是EF 还有一些其他的ORM 从JAVA 中移植过来的 有 &#xff0c; 大神自己写的也有 不管ORM 提供什么附加的 乱七八糟的功能 但是 最主要的 还是 关系映射 的事情。 我自己一直在使用ORMDapper 这个很小的ORM 第一次看到这个ORM 是通过一…

synchronized和ReentrantLock的5个区别!

作者 | 磊哥来源 | Java面试真题解析&#xff08;ID&#xff1a;aimianshi666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;在 Java 中&#xff0c;常用的锁有两种&#xff1a;synchronized&#xff08;内置锁&#xff09;和 ReentrantLock&a…

Java Random nextInt()方法与示例

随机类nextInt()方法 (Random Class nextInt() method) Syntax: 句法&#xff1a; public int nextInt();public int nextInt(int num);nextInt() method is available in java.util package. nextInt()方法在java.util包中可用。 nextInt() method is used to return the nex…

《小强升职记》读后感和思维导图

语言幽默轻松&#xff0c;寓教于乐&#xff0c;看完之后有挽起袖子大干一场的冲动&#xff0c;但是诚如书中所言&#xff0c;“不做收藏家&#xff0c;要做建筑工”&#xff0c;实践和坚持才能有所收获。第一次画思维导图(′▽〃)Xmind格式文件转载于:https://www.cnblogs.com/…

oppo后端16连问

前言 大家好&#xff0c;我是磊哥。最近有位读者去面试了oppo&#xff0c;给大家整理了面试真题的答案。希望对大家有帮助哈&#xff0c;一起学习&#xff0c;一起进步。聊聊你印象最深刻的项目&#xff0c;或者做了什么优化。你项目提到分布式锁&#xff0c;你们是怎么使用分布…

java enummap_Java EnumMap values()方法与示例

java enummapEnumMap类values()方法 (EnumMap Class values() method) values() method is available in java.util package. values()方法在java.util包中可用。 values() method is used to get all the values in a Collection view of this enum map. values()方法用于获取…

django 1.8 官方文档翻译:2-1-1 模型语法

模型 模型是你的数据的唯一的、权威的信息源。它包含你所储存数据的必要字段和行为。通常&#xff0c;每个模型对应数据库中唯一的一张表。 基础&#xff1a; 每个模型都是django.db.models.Model 的一个Python 子类。模型的每个属性都表示数据库中的一个字段。Django 提供一套…

实战!阿里神器 Seata 实现 TCC 模式解决分布式事务

今天这篇文章介绍一下Seata如何实现TCC事务模式&#xff0c;文章目录如下&#xff1a;目录什么是TCC模式&#xff1f;TCC&#xff08;Try Confirm Cancel&#xff09;方案是一种应用层面侵入业务的两阶段提交。是目前最火的一种柔性事务方案&#xff0c;其核心思想是&#xff1…

Java Dictionary get()方法与示例

字典类的get()方法 (Dictionary Class get() method) get() method is available in java.util package. get()方法在java.util包中可用。 get() method is used to get the value on the specified key element (key_ele) in this dictionary. get()方法用于获取此字典中指定键…

[CareerCup] 8.10 Implement a Hash Table 实现一个哈希表

8.10 Design and implement a hash table which uses chaining (linked lists) to handle collisions. 这道题让我们实现一个简单的哈希表&#xff0c;我们采用了最简单的那种取余映射的方式来实现&#xff0c;我们使用Cell来保存一对对的key和value的映射关系&#xff0c;然后…

Spring Boot 中实现跨域的 5 种方式,你一定要知道!

一、为什么会出现跨域问题出于浏览器的同源策略限制。同源策略&#xff08;Sameoriginpolicy&#xff09;是一种约定&#xff0c;它是浏览器最核心也最基本的安全功能&#xff0c;如果缺少了同源策略&#xff0c;则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略…

stl:queue 源码_C ++ STL中的queue :: empty()和queue :: size()

stl:queue 源码In C STL, Queue is a type of container that follows FIFO (First-in-First-out) elements arrangement i.e. the elements which inserts first will be removed first. In queue, elements are inserted at one end known as "back" and are delet…

术中导航_密码术中的计数器(CTR)模式

术中导航The Counter Mode or CTR is a simple counter based block cipher implementation in cryptography. Each or every time a counter initiated value is encrypted and given as input to XOR with plaintext or original text which results in ciphertext block. Th…