Java Unsafe 类.docx

上传人:b****7 文档编号:15280187 上传时间:2023-07-03 格式:DOCX 页数:20 大小:21.10KB
下载 相关 举报
Java Unsafe 类.docx_第1页
第1页 / 共20页
Java Unsafe 类.docx_第2页
第2页 / 共20页
Java Unsafe 类.docx_第3页
第3页 / 共20页
Java Unsafe 类.docx_第4页
第4页 / 共20页
Java Unsafe 类.docx_第5页
第5页 / 共20页
Java Unsafe 类.docx_第6页
第6页 / 共20页
Java Unsafe 类.docx_第7页
第7页 / 共20页
Java Unsafe 类.docx_第8页
第8页 / 共20页
Java Unsafe 类.docx_第9页
第9页 / 共20页
Java Unsafe 类.docx_第10页
第10页 / 共20页
Java Unsafe 类.docx_第11页
第11页 / 共20页
Java Unsafe 类.docx_第12页
第12页 / 共20页
Java Unsafe 类.docx_第13页
第13页 / 共20页
Java Unsafe 类.docx_第14页
第14页 / 共20页
Java Unsafe 类.docx_第15页
第15页 / 共20页
Java Unsafe 类.docx_第16页
第16页 / 共20页
Java Unsafe 类.docx_第17页
第17页 / 共20页
Java Unsafe 类.docx_第18页
第18页 / 共20页
Java Unsafe 类.docx_第19页
第19页 / 共20页
Java Unsafe 类.docx_第20页
第20页 / 共20页
亲,该文档总共20页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

Java Unsafe 类.docx

《Java Unsafe 类.docx》由会员分享,可在线阅读,更多相关《Java Unsafe 类.docx(20页珍藏版)》请在冰点文库上搜索。

Java Unsafe 类.docx

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

*/

publicfinalListchildren;

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

*/

privatestaticfinalMapprimitiveSizes;

static{

primitiveSizes=newHashMap(10);

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

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 医药卫生 > 基础医学

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2