Java Unsafe类实现原理及测试代码
Unsafe类介绍
第一次看到这个类时被它的名字吓到了,居然还有一个类自名Unsafe?读完本文,大家也能发现Unsafe类确实有点不那么安全,它能实现一些不那么常见的功能。
Unsafe类使Java拥有了像C语言的指针一样操作内存空间的能力,同时也带来了指针的问题。过度的使用Unsafe类会使得出错的几率变大,因此Java官方并不建议使用的,官方文档也几乎没有。Oracle正在计划从Java9中去掉Unsafe类,如果真是如此影响就太大了。
Unsafe类提供了以下这些功能:
一、内存管理。包括分配内存、释放内存等。
该部分包括了allocateMemory(分配内存)、reallocateMemory(重新分配内存)、copyMemory(拷贝内存)、freeMemory(释放内存)、getAddress(获取内存地址)、addressSize、pageSize、getInt(获取内存地址指向的整数)、getIntVolatile(获取内存地址指向的整数,并支持volatile语义)、putInt(将整数写入指定内存地址)、putIntVolatile(将整数写入指定内存地址,并支持volatile语义)、putOrderedInt(将整数写入指定内存地址、有序或者有延迟的方法)等方法。getXXX和putXXX包含了各种基本类型的操作。
利用copyMemory方法,我们可以实现一个通用的对象拷贝方法,无需再对每一个对象都实现clone方法,当然这通用的方法只能做到对象浅拷贝。
二、非常规的对象实例化。
allocateInstance()方法提供了另一种创建实例的途径。通常我们可以用new或者反射来实例化对象,使用allocateInstance()方法可以直接生成对象实例,且无需调用构造方法和其它初始化方法。
这在对象反序列化的时候会很有用,能够重建和设置final字段,而不需要调用构造方法。
三、操作类、对象、变量。
这部分包括了staticFieldOffset(静态域偏移)、defineClass(定义类)、defineAnonymousClass(定义匿名类)、ensureClassInitialized(确保类初始化)、objectFieldOffset(对象域偏移)等方法。
通过这些方法我们可以获取对象的指针,通过对指针进行偏移,我们不仅可以直接修改指针指向的数据(即使它们是私有的),甚至可以找到JVM已经认定为垃圾、可以进行回收的对象。
四、数组操作。
这部分包括了arrayBaseOffset(获取数组第一个元素的偏移地址)、arrayIndexScale(获取数组中元素的增量地址)等方法。arrayBaseOffset与arrayIndexScale配合起来使用,就可以定位数组中每个元素在内存中的位置。
由于Java的数组最大值为Integer.MAX_VALUE,使用Unsafe类的内存分配方法可以实现超大数组。实际上这样的数据就可以认为是C数组,因此需要注意在合适的时间释放内存。
五、多线程同步。包括锁机制、CAS操作等。
这部分包括了monitorEnter、tryMonitorEnter、monitorExit、compareAndSwapInt、compareAndSwap等方法。
其中monitorEnter、tryMonitorEnter、monitorExit已经被标记为deprecated,不建议使用。
Unsafe类的CAS操作可能是用的最多的,它为Java的锁机制提供了一种新的解决办法,比如AtomicInteger等类都是通过该方法来实现的。compareAndSwap方法是原子的,可以避免繁重的锁机制,提高代码效率。这是一种乐观锁,通常认为在大部分情况下不出现竞态条件,如果操作失败,会不断重试直到成功。
六、挂起与恢复。
这部分包括了park、unpark等方法。
将一个线程进行挂起是通过park方法实现的,调用park后,线程将一直阻塞直到超时或者中断等条件出现。unpark可以终止一个挂起的线程,使其恢复正常。整个并发框架中对线程的挂起操作被封装在LockSupport类中,LockSupport类中有各种版本pack方法,但最终都调用了Unsafe.park()方法。
七、内存屏障。
这部分包括了loadFence、storeFence、fullFence等方法。这是在Java8新引入的,用于定义内存屏障,避免代码重排序。
loadFence()表示该方法之前的所有load操作在内存屏障之前完成。同理storeFence()表示该方法之前的所有store操作在内存屏障之前完成。fullFence()表示该方法之前的所有load、store操作在内存屏障之前完成。
测试代码
importcom.User;
importorg.junit.Before;
importorg.junit.Test;
importsun.misc.Unsafe;
importjava.lang.reflect.Field;
importjava.util.ArrayList;
importjava.util.UUID;
importjava.util.concurrent.CountDownLatch;
classUser{
publicstaticStringUSER_CLASS_NAME="User.class";
privateintage;
privateStringname;
publicintgetAge(){
returnage;
}
publicStringgetName(){
returnname;
}
publicUser(intage,Stringname){
this.age=age;
this.name=name;
}
publicvoidsetAge(intage){
this.age=age;
}
publicvoidsetName(Stringname){
this.name=name;
}
}
publicclassLockTests{
UnsafeunSafe;
Useru=newUser(17,"zhangsan");
@Before
publicvoidbefore()throwsException{
FieldtheUnsafeField=Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
unSafe=(Unsafe)theUnsafeField.get(Unsafe.class);
}
@Test
publicvoidobjectFieldOffset()throwsException{
//unSafe偏底层的一个Java工具类
java.util.Listusers=newArrayList();
for(inti=0;i<10;i++){
FieldageField=User.class.getDeclaredField("age");
Useru=newUser(18,"daxin");
users.add(u);
//使用内存获取Userage字段在内存中的offset
//是相对地址,不是一个绝对地址
longageOffset=unSafe.objectFieldOffset(ageField);
//每次都相同
System.out.println("ageOffset="+ageOffset);
}
}
@Test
publicvoidcompareAndSwapInt()throwsException{
//unSafe偏底层的一个Java工具类
FieldageField=User.class.getDeclaredField("age");
Useru=newUser(18,"daxin");
//使用内存获取Userage字段在内存中的offset
longageOffset=unSafe.objectFieldOffset(ageField);
//修改之前的值
System.out.println(u.getAge());
//进行CAS更新,由于设置18因此CAS会成功
unSafe.compareAndSwapInt(u,ageOffset,18,20);
System.out.println(u.getAge());
//由于age设置20进行CAS失败
unSafe.compareAndSwapInt(u,ageOffset,18,22);
System.out.println(u.getAge());
}
@Test
publicvoidensureClassInitialized(){
System.out.println("====start====");
unSafe.ensureClassInitialized(ClassIsLoad.class);
//再次确认不会报错
unSafe.ensureClassInitialized(ClassIsLoad.class);
}
/**
*AQS底层的Node链表就是基于这个工具实现的。
*
*@throwsException
*/
@Test
publicvoidgetValueByFieldOffset()throwsException{
for(inti=0;i<10;i++){
Useru=newUser(18,UUID.randomUUID().toString().substring(i,20));
intage=unSafe.getInt(u,12L);
System.out.println("age="+age);
//获取名字fieldoffset
FieldnameField=User.class.getDeclaredField("name");
longnameOffset=unSafe.objectFieldOffset(nameField);
System.out.println("nameOffset="+nameOffset);
Stringname=unSafe.getObject(u,nameOffset)+"";
System.out.println("name="+name);
}
}
@Test
publicvoidpageSize(){
System.out.println("unSafe.pageSize()="+unSafe.pageSize());
}
/**
*AtomicInteger底层是基于getAndAddInt实现
*/
@Test
publicvoidgetAndAddInt()throwsInterruptedException{
Useru=newUser(17,"zhangsan");
CountDownLatchdownLatch=newCountDownLatch(10);
System.out.println("u.getAge()="+u.getAge());
for(inti=0;i<10;i++){
newThread(newRunnable(){
@Override
publicvoidrun(){
downLatch.countDown();
intval=unSafe.getAndAddInt(u,12L,1);
System.out.println(Thread.currentThread().getName()+"val="+val);
}
}).start();
}
Thread.sleep(5000);
System.out.println("u.getAge()="+u.getAge());
}
@Test
publicvoidgetAndSetInt()throwsInterruptedException{
Useru=newUser(17,"zhangsan");
CountDownLatchdownLatch=newCountDownLatch(10);
System.out.println("u.getAge()="+u.getAge());
for(inti=0;i<10;i++){
newThread(newRunnable(){
@Override
publicvoidrun(){
downLatch.countDown();
intval=unSafe.getAndSetInt(u,12L,10);
System.out.println(Thread.currentThread().getName()+"val="+val);
}
}).start();
}
Thread.sleep(5000);
System.out.println("u.getAge()="+u.getAge());
}
@Test
publicvoidgetIntVolatile(){
for(inti=0;i<10;i++){
u.setAge(i);
/**
*@paramobjtheobjectcontainingthefieldtomodify.
*@paramoffsettheoffsetoftheintegerfieldwithinobj.
*@return
*/
intage=unSafe.getIntVolatile(u,12L);
System.out.println("age="+age);
}
}
//系统负载采样的接口
@Test
publicvoidgetLoadAverage(){
double[]nums=newdouble[8];
intval=unSafe.getLoadAverage(nums,8);
System.out.println(val);
}
/**
*//内存屏障,禁止load操作重排序。屏障前的load操作不能被重排序到屏障后,屏障后的load操作不能被重排序到屏障前
*publicnativevoidloadFence();
*
*
*参见:https://tech.meituan.com/2019/02/14/talk-about-java-magic-class-unsafe.html
*/
@Test
publicvoidloadFence(){
//java.util.concurrent.locks.StampedLock.validate
unSafe.loadFence();
}
/**
*//内存屏障,禁止store操作重排序。屏障前的store操作不能被重排序到屏障后,屏障后的store操作不能被重排序到屏障前
*publicnativevoidstoreFence();
*参见:https://tech.meituan.com/2019/02/14/talk-about-java-magic-class-unsafe.html
*/
@Test
publicvoidstoreFence(){
}
/**
*//内存屏障,禁止load、store操作重排序
*publicnativevoidfullFence();
*参见:https://tech.meituan.com/2019/02/14/talk-about-java-magic-class-unsafe.html
*/
@Test
publicvoidfullFence(){
}
@Test
publicvoidshouldBeInitialized(){
booleanshouldBeInitialized=unSafe.shouldBeInitialized(String.class);
System.out.println(shouldBeInitialized);
shouldBeInitialized=unSafe.shouldBeInitialized(User.class);
System.out.println(shouldBeInitialized);
}
/**
*synchronized的一种实现获取锁
*
*@throwsInterruptedException
*/
@Test
publicvoidmonitorEnter()throwsInterruptedException{
unSafe.monitorEnter(u);
newThread(newRunnable(){
@Override
publicvoidrun(){
synchronized(u){
System.out.println("==ulockgot==");
}
}
}).start();
Thread.sleep(2000);
unSafe.monitorExit(u);
}
@Test
publicvoidcompareAndSwap(){
//unSafe.compareAndSwapInt(对象,对象中的字段偏移,期望值,设置值)
//unSafe.compareAndSwapLong(对象,对象中的字段偏移,期望值,设置值)
//unSafe.compareAndSwapObject(对象,对象中的字段偏移,期望值,设置值)
}
@Test
publicvoidt(){
//方法签名
//publicvoidcopyMemory(ObjectsrcBase,longsrcOffset,ObjectdestBase,longdestOffset,longbytes)
//unSafe.copyMemory();
}
}
classClassIsLoad{
static{
System.out.println("ClassIsLoadclassIsLoad!");
}
}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。