一篇看懂Java中的Unsafe类
前言
本文主要给大家介绍了关于Java中Unsafe类的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧
1.Unsafe类介绍
Unsafe类是在sun.misc包下,不属于Java标准。但是很多Java的基础类库,包括一些被广泛使用的高性能开发库都是基于Unsafe类开发的,比如Netty、Hadoop、Kafka等。
使用Unsafe可用来直接访问系统内存资源并进行自主管理,Unsafe类在提升Java运行效率,增强Java语言底层操作能力方面起了很大的作用。
Unsafe可认为是Java中留下的后门,提供了一些低层次操作,如直接内存访问、线程调度等。
官方并不建议使用Unsafe。
下面是使用Unsafe的一些例子。
1.1实例化私有类
importjava.lang.reflect.Field;
importsun.misc.Unsafe;
publicclassUnsafePlayer{
publicstaticvoidmain(String[]args)throwsException{
//通过反射实例化Unsafe
Fieldf=Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafeunsafe=(Unsafe)f.get(null);
//实例化Player
Playerplayer=(Player)unsafe.allocateInstance(Player.class);
player.setName("lilei");
System.out.println(player.getName());
}
}
classPlayer{
privateStringname;
privatePlayer(){}
publicStringgetName(){
returnname;
}
publicvoidsetName(Stringname){
this.name=name;
}
}
1.2CAS操作,通过内存偏移地址修改变量值
java并发包中的SynchronousQueue中的TransferStack中使用CAS更新栈顶。
/Unsafemechanics
privatestaticfinalsun.misc.UnsafeUNSAFE;
privatestaticfinallongheadOffset;
static{
try{
UNSAFE=sun.misc.Unsafe.getUnsafe();
Class>k=TransferStack.class;
headOffset=UNSAFE.objectFieldOffset
(k.getDeclaredField("head"));
}catch(Exceptione){
thrownewError(e);
}
}
//栈顶
volatileSNodehead;
//更新栈顶
booleancasHead(SNodeh,SNodenh){
returnh==head&&
UNSAFE.compareAndSwapObject(this,headOffset,h,nh);
}
1.3直接内存访问
Unsafe的直接内存访问:用Unsafe开辟的内存空间不占用Heap空间,当然也不具有自动内存回收功能。做到像C一样自由利用系统内存资源。
2.Unsafe类源码分析
Unsafe的大部分API都是native的方法,主要包括以下几类:
1)Class相关。主要提供Class和它的静态字段的操作方法。
2)Object相关。主要提供Object和它的字段的操作方法。
3)Arrray相关。主要提供数组及其中元素的操作方法。
4)并发相关。主要提供低级别同步原语,如CAS、线程调度、volatile、内存屏障等。
5)Memory相关。提供了直接内存访问方法(绕过Java堆直接操作本地内存),可做到像C一样自由利用系统内存资源。
6)系统相关。主要返回某些低级别的内存信息,如地址大小、内存页大小。
2.1Class相关
//静态属性的偏移量,用于在对应的Class对象中读写静态属性 publicnativelongstaticFieldOffset(Fieldf); publicnativeObjectstaticFieldBase(Fieldf); //判断是否需要初始化一个类 publicnativebooleanshouldBeInitialized(Class>c); //确保类被初始化 publicnativevoidensureClassInitialized(Class>c); //定义一个类,可用于动态创建类 publicnativeClass>defineClass(Stringname,byte[]b,intoff,intlen, ClassLoaderloader, ProtectionDomainprotectionDomain); //定义一个匿名类,可用于动态创建类 publicnativeClass>defineAnonymousClass(Class>hostClass,byte[]data,Object[]cpPatches);
2.2Object相关
Java中的基本类型(boolean、byte、char、short、int、long、float、double)及对象引用类型都有以下方法。
//获得对象的字段偏移量 publicnativelongobjectFieldOffset(Fieldf); //获得给定对象地址偏移量的int值 publicnativeintgetInt(Objecto,longoffset); //设置给定对象地址偏移量的int值 publicnativevoidputInt(Objecto,longoffset,intx);
//创建对象,但并不会调用其构造方法。如果类未被初始化,将初始化类。 publicnativeObjectallocateInstance(Class>cls) throwsInstantiationException;
2.3数组相关
/**
*Reporttheoffsetofthefirstelementinthestorageallocationofa
*givenarrayclass.If{@link#arrayIndexScale}returnsanon-zerovalue
*forthesameclass,youmayusethatscalefactor,togetherwiththis
*baseoffset,toformnewoffsetstoaccesselementsofarraysofthe
*givenclass.
*
*@see#getInt(Object,long)
*@see#putInt(Object,long,int)
*/
//返回数组中第一个元素的偏移地址
publicnativeintarrayBaseOffset(Class>arrayClass);
//boolean、byte、short、char、int、long、float、double,及对象类型均有以下方法
/**Thevalueof{@codearrayBaseOffset(boolean[].class)}*/
publicstaticfinalintARRAY_BOOLEAN_BASE_OFFSET
=theUnsafe.arrayBaseOffset(boolean[].class);
/**
*Reportthescalefactorforaddressingelementsinthestorage
*allocationofagivenarrayclass.However,arraysof"narrow"types
*willgenerallynotworkproperlywithaccessorslike{@link
*#getByte(Object,int)},sothescalefactorforsuchclassesisreported
*aszero.
*
*@see#arrayBaseOffset
*@see#getInt(Object,long)
*@see#putInt(Object,long,int)
*/
//返回数组中每一个元素占用的大小
publicnativeintarrayIndexScale(Class>arrayClass);
//boolean、byte、short、char、int、long、float、double,及对象类型均有以下方法
/**Thevalueof{@codearrayIndexScale(boolean[].class)}*/
publicstaticfinalintARRAY_BOOLEAN_INDEX_SCALE
=theUnsafe.arrayIndexScale(boolean[].class);
通过arrayBaseOffset和arrayIndexScale可定位数组中每个元素在内存中的位置。
2.4并发相关
2.4.1CAS相关
CAS:CompareAndSwap,内存偏移地址offset,预期值expected,新值x。如果变量在当前时刻的值和预期值expected相等,尝试将变量的值更新为x。如果更新成功,返回true;否则,返回false。
//更新变量值为x,如果当前值为expected //o:对象offset:偏移量expected:期望值x:新值 publicfinalnativebooleancompareAndSwapObject(Objecto,longoffset, Objectexpected, Objectx); publicfinalnativebooleancompareAndSwapInt(Objecto,longoffset, intexpected, intx); publicfinalnativebooleancompareAndSwapLong(Objecto,longoffset, longexpected, longx);
从Java8开始,Unsafe中提供了以下方法:
//增加
publicfinalintgetAndAddInt(Objecto,longoffset,intdelta){
intv;
do{
v=getIntVolatile(o,offset);
}while(!compareAndSwapInt(o,offset,v,v+delta));
returnv;
}
publicfinallonggetAndAddLong(Objecto,longoffset,longdelta){
longv;
do{
v=getLongVolatile(o,offset);
}while(!compareAndSwapLong(o,offset,v,v+delta));
returnv;
}
//设置
publicfinalintgetAndSetInt(Objecto,longoffset,intnewValue){
intv;
do{
v=getIntVolatile(o,offset);
}while(!compareAndSwapInt(o,offset,v,newValue));
returnv;
}
publicfinallonggetAndSetLong(Objecto,longoffset,longnewValue){
longv;
do{
v=getLongVolatile(o,offset);
}while(!compareAndSwapLong(o,offset,v,newValue));
returnv;
}
publicfinalObjectgetAndSetObject(Objecto,longoffset,ObjectnewValue){
Objectv;
do{
v=getObjectVolatile(o,offset);
}while(!compareAndSwapObject(o,offset,v,newValue));
returnv;
2.4.2线程调度相关
//取消阻塞线程 publicnativevoidunpark(Objectthread); //阻塞线程 publicnativevoidpark(booleanisAbsolute,longtime); //获得对象锁 publicnativevoidmonitorEnter(Objecto); //释放对象锁 publicnativevoidmonitorExit(Objecto); //尝试获取对象锁,返回true或false表示是否获取成功 publicnativebooleantryMonitorEnter(Objecto);
2.4.3volatile相关读写
Java中的基本类型(boolean、byte、char、short、int、long、float、double)及对象引用类型都有以下方法。
//从对象的指定偏移量处获取变量的引用,使用volatile的加载语义 //相当于getObject(Object,long)的volatile版本 publicnativeObjectgetObjectVolatile(Objecto,longoffset); //存储变量的引用到对象的指定的偏移量处,使用volatile的存储语义 //相当于putObject(Object,long,Object)的volatile版本 publicnativevoidputObjectVolatile(Objecto,longoffset,Objectx);
/**
*Versionof{@link#putObjectVolatile(Object,long,Object)}
*thatdoesnotguaranteeimmediatevisibilityofthestoreto
*otherthreads.Thismethodisgenerallyonlyusefulifthe
*underlyingfieldisaJavavolatile(orifanarraycell,one
*thatisotherwiseonlyaccessedusingvolatileaccesses).
*/
publicnativevoidputOrderedObject(Objecto,longoffset,Objectx);
/**Ordered/Lazyversionof{@link#putIntVolatile(Object,long,int)}*/
publicnativevoidputOrderedInt(Objecto,longoffset,intx);
/**Ordered/Lazyversionof{@link#putLongVolatile(Object,long,long)}*/
publicnativevoidputOrderedLong(Objecto,longoffset,longx);
2.4.4内存屏障相关
Java8引入,用于定义内存屏障,避免代码重排序。
//内存屏障,禁止load操作重排序,即屏障前的load操作不能被重排序到屏障后,屏障后的load操作不能被重排序到屏障前 publicnativevoidloadFence(); //内存屏障,禁止store操作重排序,即屏障前的store操作不能被重排序到屏障后,屏障后的store操作不能被重排序到屏障前 publicnativevoidstoreFence(); //内存屏障,禁止load、store操作重排序 publicnativevoidfullFence();
2.5直接内存访问(非堆内存)
allocateMemory所分配的内存需要手动free(不被GC回收)
//(boolean、byte、char、short、int、long、float、double)都有以下get、put两个方法。
//获得给定地址上的int值
publicnativeintgetInt(longaddress);
//设置给定地址上的int值
publicnativevoidputInt(longaddress,intx);
//获得本地指针
publicnativelonggetAddress(longaddress);
//存储本地指针到给定的内存地址
publicnativevoidputAddress(longaddress,longx);
//分配内存
publicnativelongallocateMemory(longbytes);
//重新分配内存
publicnativelongreallocateMemory(longaddress,longbytes);
//初始化内存内容
publicnativevoidsetMemory(Objecto,longoffset,longbytes,bytevalue);
//初始化内存内容
publicvoidsetMemory(longaddress,longbytes,bytevalue){
setMemory(null,address,bytes,value);
}
//内存内容拷贝
publicnativevoidcopyMemory(ObjectsrcBase,longsrcOffset,
ObjectdestBase,longdestOffset,
longbytes);
//内存内容拷贝
publicvoidcopyMemory(longsrcAddress,longdestAddress,longbytes){
copyMemory(null,srcAddress,null,destAddress,bytes);
}
//释放内存
publicnativevoidfreeMemory(longaddress);
2.6系统相关
//返回指针的大小。返回值为4或8。
publicnativeintaddressSize();
/**Thevalueof{@codeaddressSize()}*/
publicstaticfinalintADDRESS_SIZE=theUnsafe.addressSize();
//内存页的大小。
publicnativeintpageSize();
3.参考资料
https://www.nhooo.com/article/140709.htm 说一说Java中的Unsafe类
https://www.nhooo.com/article/140721.htmjava魔法类:sun.misc.Unsafe
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。