Java Unsafe 类.docx
- 文档编号:15280187
- 上传时间:2023-07-03
- 格式:DOCX
- 页数:20
- 大小:21.10KB
Java Unsafe 类.docx
《Java Unsafe 类.docx》由会员分享,可在线阅读,更多相关《Java Unsafe 类.docx(20页珍藏版)》请在冰点文库上搜索。
JavaUnsafe类
JavaUnsafe类
Unsafe类是啥?
Java最初被设计为一种安全的受控环境。
尽管如此,JavaHotSpot还是包含了一个“后门”,提供了一些可以直接操控内存和线程的低层次操作。
这个后门类——sun.misc.Unsafe——被JDK广泛用于自己的包中,如java.nio和java.util.concurrent。
但是丝毫不建议在生产环境中使用这个后门。
因为这个API十分不安全、不轻便、而且不稳定。
这个不安全的类提供了一个观察HotSpotJVM内部结构并且可以对其进行修改。
有时它可以被用来在不适用C++调试的情况下学习虚拟机内部结构,有时也可以被拿来做性能监控和开发工具。
为什么叫Unsafe?
Java官方不推荐使用Unsafe类,因为官方认为,这个类别人很难正确使用,非正确使用会给JVM带来致命错误。
而且未来Java可能封闭丢弃这个类。
如何使用Unsafe?
1.获取Unsafe实例:
通读Unsafe源码,Unsafe提供了一个私有的静态实例,并且通过检查classloader是否为null来避免java程序直接使用unsafe:
//Unsafe源码
privatestaticfinalUnsafetheUnsafe;
@CallerSensitive
publicstaticUnsafegetUnsafe(){
Classvar0=Reflection.getCallerClass();
if(var0.getClassLoader()!
=null){
thrownewSecurityException("Unsafe");
}else{
returntheUnsafe;
}
}
我们可以通过如下代码反射获取Unsafe静态类:
/**
*获取Unsafe
*/
Fieldf=null;
Unsafeunsafe=null;
try{
f=Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
unsafe=(Unsafe)f.get(null);
}catch(NoSuchFieldExceptione){
e.printStackTrace();
}catch(IllegalAccessExceptione){
e.printStackTrace();
}
2.通过Unsafe分配使用堆外内存:
C++中有malloc,realloc和free方法来操作内存。
在Unsafe类中对应为:
//分配var1字节大小的内存,返回起始地址偏移量
publicnativelongallocateMemory(longvar1);
//重新给var1起始地址的内存分配长度为var3字节大小的内存,返回新的内存起始地址偏移量
publicnativelongreallocateMemory(longvar1,longvar3);
//释放起始地址为var1的内存
publicnativevoidfreeMemory(longvar1);
分配内存方法还有重分配内存方法都是分配的堆外内存,返回的是一个long类型的地址偏移量。
这个偏移量在你的Java程序中每块内存都是唯一的。
举例:
/**
*在堆外分配一个byte
*/
longallocatedAddress=unsafe.allocateMemory(1L);
unsafe.putByte(allocatedAddress,(byte)100);
byteshortValue=unsafe.getByte(allocatedAddress);
System.out.println(newStringBuilder().append("Address:
").append(allocatedAddress).append("Value:
").append(shortValue));
/**
*重新分配一个long
*/
allocatedAddress=unsafe.reallocateMemory(allocatedAddress,8L);
unsafe.putLong(allocatedAddress,1024L);
longlongValue=unsafe.getLong(allocatedAddress);
System.out.println(newStringBuilder().append("Address:
").append(allocatedAddress).append("Value:
").append(longValue));
/**
*Free掉,这个数据可能脏掉
*/
unsafe.freeMemory(allocatedAddress);
longValue=unsafe.getLong(allocatedAddress);
System.out.println(newStringBuilder().append("Address:
").append(allocatedAddress).append("Value:
").append(longValue));
输出:
Address:
46490464Value:
100
Address:
46490480Value:
1024
Address:
46490480Value:
22
3.操作类对象
我们可以通过Unsafe类来操作修改某一field。
原理是首先获取对象的基址(对象在内存的偏移量起始地址)。
之后获取某个filed在这个对象对应的类中的偏移地址,两者相加修改。
/**
*获取类的某个对象的某个field偏移地址
*/
try{
f=SampleClass.class.getDeclaredField("i");
}catch(NoSuchFieldExceptione){
e.printStackTrace();
}
longiFiledAddressShift=unsafe.objectFieldOffset(f);
SampleClasssampleClass=newSampleClass();
//获取对象的偏移地址,需要将目标对象设为辅助数组的第一个元素(也是唯一的元素)。
由于这是一个复杂类型元素(不是基本数据类型),它的地址存储在数组的第一个元素。
然后,获取辅助数组的基本偏移量。
数组的基本偏移量是指数组对象的起始地址与数组第一个元素之间的偏移量。
ObjecthelperArray[]=newObject[1];
helperArray[0]=sampleClass;
longbaseOffset=unsafe.arrayBaseOffset(Object[].class);
longaddressOfSampleClass=unsafe.getLong(helperArray,baseOffset);
inti=unsafe.getInt(addressOfSampleClass+iFiledAddressShift);
System.out.println(newStringBuilder().append("FieldIAddress:
").append(addressOfSampleClass).append("+").append(iFiledAddressShift).append("Value:
").append(i));
输出:
FieldIAddress:
3610777760+24Value:
5
4.线程挂起和恢复
将一个线程进行挂起是通过park方法实现的,调用park后,线程将一直阻塞直到超时或者中断等条件出现。
unpark可以终止一个挂起的线程,使其恢复正常。
整个并发框架中对线程的挂起操作被封装在LockSupport类中,LockSupport类中有各种版本pack方法,但最终都调用了Unsafe.park()方法。
publicclassLockSupport{
publicstaticvoidunpark(Threadthread){
if(thread!
=null)
unsafe.unpark(thread);
}
publicstaticvoidpark(Objectblocker){
Threadt=Thread.currentThread();
setBlocker(t,blocker);
unsafe.park(false,0L);
setBlocker(t,null);
}
publicstaticvoidparkNanos(Objectblocker,longnanos){
if(nanos>0){
Threadt=Thread.currentThread();
setBlocker(t,blocker);
unsafe.park(false,nanos);
setBlocker(t,null);
}
}
publicstaticvoidparkUntil(Objectblocker,longdeadline){
Threadt=Thread.currentThread();
setBlocker(t,blocker);
unsafe.park(true,deadline);
setBlocker(t,null);
}
publicstaticvoidpark(){
unsafe.park(false,0L);
}
publicstaticvoidparkNanos(longnanos){
if(nanos>0)
unsafe.park(false,nanos);
}
publicstaticvoidparkUntil(longdeadline){
unsafe.park(true,deadline);
}
}
5.CAS操作
/**
*比较obj的offset处内存位置中的值和期望的值,如果相同则更新。
此更新是不可中断的。
*
*@paramobj需要更新的对象
*@paramoffsetobj中整型field的偏移量
*@paramexpect希望field中存在的值
*@paramupdate如果期望值expect与field的当前值相同,设置filed的值为这个新值
*@return如果field的值被更改返回true
*/
publicnativebooleancompareAndSwapInt(Objectobj,longoffset,intexpect,intupdate);
6.Clone
如何实现浅克隆?
在clone(){…}方法中调用super.clone(),对吗?
这里存在的问题是首先你必须继续Cloneable接口,并且在所有你需要做浅克隆的对象中实现clone()方法,对于一个懒懒的程序员来说,这个工作量太大了。
我不推荐上面的做法而是直接使用Unsafe,我们可以仅使用几行代码就实现浅克隆,并且它可以像某些工具类一样用于任意类的克隆。
首先,我们需要一个计算Object大小的工具类:
classObjectInfo{
/**
*Fieldname
*/
publicfinalStringname;
/**
*Fieldtypename
*/
publicfinalStringtype;
/**
*Fielddataformattedasstring
*/
publicfinalStringcontents;
/**
*Fieldoffsetfromthestartofparentobject
*/
publicfinalintoffset;
/**
*Memoryoccupiedbythisfield
*/
publicfinalintlength;
/**
*Offsetofthefirstcellinthearray
*/
publicfinalintarrayBase;
/**
*Sizeofacellinthearray
*/
publicfinalintarrayElementSize;
/**
*Memoryoccupiedbyunderlyingarray(shallow),ifthisisarraytype
*/
publicfinalintarraySize;
/**
*Thisobjectfields
*/
publicfinalList
publicObjectInfo(Stringname,Stringtype,Stringcontents,intoffset,intlength,intarraySize,
intarrayBase,intarrayElementSize){
this.name=name;
this.type=type;
this.contents=contents;
this.offset=offset;
this.length=length;
this.arraySize=arraySize;
this.arrayBase=arrayBase;
this.arrayElementSize=arrayElementSize;
children=newArrayList
(1);
}
publicvoidaddChild(finalObjectInfoinfo){
if(info!
=null)
children.add(info);
}
/**
*Getthefullamountofmemoryoccupiedbyagivenobject.Thisvaluemaybeslightlylessthan
*anactualvaluebecausewedon'tworryaboutmemoryalignment-possiblepaddingafterthelastobjectfield.
*
*Theresultisequaltothelastfieldoffset+lastfieldlength+allarraysizes+allchildobjectsdeepsizes
*
*@returnDeepobjectsize
*/
publiclonggetDeepSize(){
//returnlength+arraySize+getUnderlyingSize(arraySize!
=0);
returnaddPaddingSize(arraySize+getUnderlyingSize(arraySize!
=0));
}
longsize=0;
privatelonggetUnderlyingSize(finalbooleanisArray){
//longsize=0;
for(finalObjectInfochild:
children)
size+=child.arraySize+child.getUnderlyingSize(child.arraySize!
=0);
if(!
isArray&&!
ldren.isEmpty()){
inttempSize=children.get(children.size()-1).offset+children.get(children.size()-1).length;
size+=addPaddingSize(tempSize);
}
returnsize;
}
privatestaticfinalclassOffsetComparatorimplementsComparator
@Override
publicintcompare(finalObjectInfoo1,finalObjectInfoo2){
returno1.offset-o2.offset;//safebecauseoffsetsaresmallnon-negativenumbers
}
}
//sortallchildrenbytheiroffset
publicvoidsort(){
Collections.sort(children,newOffsetComparator());
}
@Override
publicStringtoString(){
finalStringBuildersb=newStringBuilder();
toStringHelper(sb,0);
returnsb.toString();
}
privatevoidtoStringHelper(finalStringBuildersb,finalintdepth){
depth(sb,depth).append("name=").append(name).append(",type=").append(type)
.append(",contents=").append(contents).append(",offset=").append(offset)
.append(",length=").append(length);
if(arraySize>0){
sb.append(",arrayBase=").append(arrayBase);
sb.append(",arrayElemSize=").append(arrayElementSize);
sb.append(",arraySize=").append(arraySize);
}
for(finalObjectInfochild:
children){
sb.append('\n');
child.toStringHelper(sb,depth+1);
}
}
privateStringBuilderdepth(finalStringBuildersb,finalintdepth){
for(inti=0;i sb.append("\t"); returnsb; } privatelongaddPaddingSize(longsize){ if(size%8! =0){ return(size/8+1)*8; } returnsize; } } classClassIntrospector{ privatestaticfinalUnsafeunsafe; /** *SizeofanyObjectreference */ privatestaticfinalintobjectRefSize; static{ try{ Fieldfield=Unsafe.class.getDeclaredField("theUnsafe"); field.setAccessible(true); unsafe=(Unsafe)field.get(null); objectRefSize=unsafe.arrayIndexScale(Object[].class); }catch(Exceptione){ thrownewRuntimeException(e); } } /** *Sizesofallprimitivevalues */ privatestaticfinalMap static{ primitiveSizes=newHashMap primitiveSizes.put(byte.class,1); primitiveSizes.put(char.class,2); primitiveSizes.put(int.class,4); primitiveSizes.put(long.class,8); primitiveSizes.put(float.class,4); primitiveSizes.put(double.class,8); primitiveSizes.put(boolean.class,1); } /** *GetobjectinformationforanyJavaobject.Donotpassprimitivesto *thismethodbecausetheywillboxedandtheinformationyouwillgetwill *berelatedtoaboxedversionofyourvalue. * *@paramobjObjecttointrospect *@returnObjectinfo *@throwsIllegalAccessException */ publicObjectInfointrospect(finalObjectobj) throwsIllegalAccessException{ try{ returnintrospect(obj,null); }finally{//cleanvisitedcachebeforereturninginordertomake //thisobjectr
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Java Unsafe