① 如何使用Unsafe操作内存中的java类和对象
在Java中最直接的内存操作方法是什么?
Java最初被设计为一种安全的受控环境。尽管如此,Java HotSpot还是包含了一个“后门”,提供了一些可以直接操控内存和线程的低层次操作。这个后门类——sun.misc.Unsafe——被JDK广泛用于自己的包中,如java.nio和java.util.concurrent。但是丝毫不建议在生产环境中使用这个后门。因为这个API十分不安全、不轻便、而且不稳定。这个不安全的类提供了一个观察HotSpot JVM内部结构并且可以对其进行修改。有时它可以被用来在不适用C++调试的情况下学习虚拟机内部结构,有时也可以被拿来做性能监控和开发工具。
为何变得不安全
sun.misc.Unsafe这个类是如此地不安全,以至于JDK开发者增加了很多特殊限制来访问它。它的构造器是私有的,工厂方法getUnsafe()的调用器只能被Bootloader加载。如你在下面代码片段的第8行所见,这个家伙甚至没有被任何类加载器加载,所以它的类加载器是null。它会抛出SecurityException 异常来阻止侵入者。
② JAVA POI 使用addMergedRegionUnsafe合并单元格操作导致导出时间大大增加,怎么解决
可以逻辑合并,直接把excel文件后缀名改zip,进去后进入目录xl/sheet目录下,打开sheet1.xml如下
这是横向合并,spans="1:2"中的1代表第1行2代表合并ab两列,具体可以根据代码逻辑修改,这种直接修改源文件的方式是效率最快的,逻辑也是最复杂的,具体看自己取舍吧
③ Java为什么会引入及如何使用Unsafe
sun.misc.Unsafe至少从2004年Java1.4开始就存在于Java中了。在Java9中,为了提高JVM的可维护性,Unsafe和许多其他的东西一起都被作为内部使用类隐藏起来了。但是究竟是什么取代Unsafe不得而知,个人推测会有不止一样来取代它,那么问题来了,到底为什么要使用Unsafe?
做一些Java语言不允许但是又十分有用的事情
很多低级语言中可用的技巧在Java中都是不被允许的。对大多数开发者而言这是件好事,既可以拯救你,也可以拯救你的同事们。同样也使得导入开源代码更容易了,因为你能掌握它们可以造成的最大的灾难上限。或者至少明确你可以不小心失误的界限。如果你尝试地足够努力,你也能造成损害。
那你可能会奇怪,为什么还要去尝试呢?当建立库时,Unsafe中很多(但不是所有)方法都很有用,且有些情况下,除了使用JNI,没有其他方法做同样的事情,即使它可能会更加危险同时也会失去Java的“一次编译,永久运行”的跨平台特性。
对象的反序列化
当使用框架反序列化或者构建对象时,会假设从已存在的对象中重建,你期望使用反射来调用类的设置函数,或者更准确一点是能直接设置内部字段甚至是final字段的函数。问题是你想创建一个对象的实例,但你实际上又不需要构造函数,因为它可能会使问题更加困难而且会有副作用。
public class A implements Serializable {
private final int num;
public A(int num) {
System.out.println("Hello Mum");
this.num = num;
}
public int getNum() {
return num;
}
}
在这个类中,应该能够重建和设置final字段,但如果你不得不调用构造函数时,它就可能做一些和反序列化无关的事情。有了这些原因,很多库使用Unsafe创建实例而不是调用构造函数。
Unsafe unsafe = getUnsafe();
Class aClass = A.class;
A a = (A) unsafe.allocateInstance(aClass);
调用allocateInstance函数避免了在我们不需要构造函数的时候却调用它。
线程安全的直接获取内存
Unsafe的另外一个用途是线程安全的获取非堆内存。ByteBuffer函数也能使你安全的获取非堆内存或是DirectMemory,但它不会提供任何线程安全的操作。你在进程间共享数据时使用Unsafe尤其有用。
import sun.misc.Unsafe;
import sun.nio.ch.DirectBuffer;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Field;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
public class PingPongMapMain {
public static void main(String... args) throws IOException {
boolean odd;
switch (args.length < 1 ? "usage" : args[0].toLowerCase()) {
case "odd":
odd = true;
break;
case "even":
odd = false;
break;
default:
System.err.println("Usage: java PingPongMain [odd|even]");
return;
}
int runs = 10000000;
long start = 0;
System.out.println("Waiting for the other odd/even");
File counters = new File(System.getProperty("java.io.tmpdir"), "counters.deleteme");
counters.deleteOnExit();
try (FileChannel fc = new RandomAccessFile(counters, "rw").getChannel()) {
MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, 1024);
long address = ((DirectBuffer) mbb).address();
for (int i = -1; i < runs; i++) {
for (; ; ) {
long value = UNSAFE.getLongVolatile(null, address);
boolean isOdd = (value & 1) != 0;
if (isOdd != odd)
// wait for the other side.
continue;
// make the change atomic, just in case there is more than one odd/even process
if (UNSAFE.compareAndSwapLong(null, address, value, value + 1))
break;
}
if (i == 0) {
System.out.println("Started");
start = System.nanoTime();
}
}
}
System.out.printf("... Finished, average ping/pong took %,d ns%n",
(System.nanoTime() - start) / runs);
}
static final Unsafe UNSAFE;
static {
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
UNSAFE = (Unsafe) theUnsafe.get(null);
} catch (Exception e) {
throw new AssertionError(e);
}
}
}
当你分别在两个程序,一个输入odd一个输入even,中运行时,可以看到两个进程都是通过持久化共享内存交换数据的。
在每个程序中,将相同的磁盘缓存映射到进程中。内存中实际上只有一份文件的副本存在。这意味着内存可以共享,前提是你使用线程安全的操作,比如volatile变量和CAS操作。(译注:CAS Compare and Swap 无锁算法)
在两个进程之间有83ns的往返时间。当考虑到System V IPC(进程间通信)大约需要2500ns,而且用IPC volatile替代persisted内存,算是相当快的了。
Unsafe适合在工作中使用吗?
个人不建议直接使用Unsafe。它远比原生的Java开发所需要的测试多。基于这个原因建议还是使用经过测试的库。如果你只是想自己用Unsafe,建议你最好在一个独立的类库中进行全面的测试。这限制了Unsafe在你的应用程序中的使用方式,但会给你一个更安全的Unsafe。
总结
Unsafe在Java中是很有趣的一个存在,你可以一个人在家里随便玩玩。它也有一些工作的应用程序特别是在写底层库的时候,但总的来说,使用经过测试的Unsafe库比直接用要好。
④ 求教java中的unsafe.allocateMemory 会导致内存申请失败吗
一:Java内存区域与内存溢出异常
在运行Java程序时,Java虚拟机会把管理的内存划分为若干个不同的数据区域。
Java虚拟机运行时数据区
数据区域图中,除了方法区和堆区是线程共享区外,其他三个是线程隔离的数据区(private)
程序计数器(Program Counter Register):属于线程私有的,占用的内存空间较少,可以看成是当前线程所执行字节码的行号指示器,字节码解释器工作时就是通过改变这个计数器的值来选择下一条,需要执行的字节码指令,分支,循环,跳转,异常处理,线程恢复等基础功能需要依赖这个计数器来完成,这个区域是jvm规范中没有规定任何OutOfMemoryError情况区域。
虚拟机栈:和程序计数器一样,都属于线程私有,生命周期与线程相同,描述的是java方法执行的内存模型,每个方法执行都会创建一个栈帧,用于存储局部变量表,操作栈,动态链接,方法出口等信息,每一个方法被调用直至执行完成的过程,就对应一个栈帧在jvm stack 从入栈到出栈的过程.局部变量表存放了编译期可知的各种数据基本类型(Boolean,byte,char,short,int,float,long,double),以及对象的引用。这个区域中定义了2种异常情况,如果线程请求的栈深度大于jvm所允许的深度,将抛出StackOverflowError异常,如果jvm可以动态扩张,当扩张无法申请到足够的内存空间是会抛出OutOfMemoryError异常。(这些数据区域异常将在下面的例子都讲到)。
本地方法栈:与虚拟机栈比较相似。其区别:虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则为虚拟机使用Native方法服务。
堆(Heap):jvm中内存占用最大的一块,是所有线程共享的一块内存区域.在jvm启动时创建,存放的是所有对象实例(或数组),所有的对象实例都在这里进行动态分配,当类空间无法再扩张会抛出OutOfMemoryError异常。Java堆是垃圾收集器管理的主要区域,而收集器采用分代收集算法。
方法区(Method Area):与堆类似,也是各个线程共享的内存区域,主要用来存储加载的类信息,常量,静态变量,即时编译器编译后的代码等数据,当方法区无法满足内存分配时,也抛出OutOfMemoryError异常。运行时常量池是方法区的一部分,用于存放编译期生成的各种字面量和符号引用相对于Class文件常量池的重要特征是具备动态性(常量并非强制编译期产生,运行期间也可以新增,例如String类的intern()方法)。
直接内存(DirectMemort):并不属于数据区,也不属于java定义的内存区域。由于NIO(New Input/Output)类,引入了一种基于通道与缓冲区(Buffer)的I/O方式。
对象访问
Object object = new Object();
Object object 这部分存储在java栈的本地变量表中,作为一个引用(reference)类型存在。
new Object() 这部分存储在java堆中,形成了一块存储了Object类型所有的实例数据值的结构化内存,动态变化,长度不固定。
方法区:在java堆中,必须要找到此对象类型数据,比如,对象类型,基类,实现的接口,方法等都存放在方法区。
对象访问方式有两种:句柄和直接指针。
句柄:reference中存储是对象的句柄地址,而句柄包含了对象实例数据和类型数据各自具体地址信息。好处:在对象移动时只需改变句柄中的实例数据指针,reference本身不需要修改。
直接指针:reference中直接存储的就是对象地址。好处:速度快,它节省了一次指针定位的时间开销。
实战:OutOfMemoryError异常
1. Java堆溢出
调整虚拟机最小值(-Xms)和最大值(-Xmx),并通过参数-XX:+HeapDumpOnOutOfMemoryError生成快照。要解决这个区域的异常,通过内存映像分析工具对快照分析,确认内存中的对象是否是必要的,分清楚出现了内存泄露还是内存溢出。若是内存泄露,通过工具查看泄露对象到GCRoots引用链,找到泄露对象是通过怎样的路径与GCRoots相关联并导致垃圾收集器无法自动回收。若不存在泄露,则检查虚拟机堆参数与机器物理内存对比看是否还能调大或从代码上检查某些对象生命周期是否过长,尝试减少程序运行期的内存消耗。
2. 虚拟机栈和本地方法栈溢出
调节栈容量大小(-Xss)。如果线程请求的栈深度大于虚拟机所允许的最大深度,将会抛出StackOverflowError异常。使用-Xss参数减小栈内存容量或者增加此方法帧中本地变量表的程度都使栈深度缩小。
3. 运行时常量池溢出
调节参数-XX:PermSize和-XX:MaxPermSize限制方法区的大小,然后使用String.intern()这个Native方法向常量池中添加内容。运行时常量池溢出,在OutOfMemoryError后面跟随提示信息是“PermGen space”,说明运行时常量池属于方法区(HotSpot虚拟机的永久代)的一部分。
4. 方法区溢出
同样使用参数-XX:PermSize和-XX:MaxPermSize限制方法区的大小,然后不断产生大量的class来加载到内存,从而出现OutOfMemoryError。所以在经常动态生成大量Class的应用中,需要特别注意类的回收状况。
5. 本机直接内存溢出
通过参数-XX:MaxDirectMemorySize指定DirectMemory容量,若不指定则与Java堆最大值一样。可以直接通过反射获取Unsafe实例并进行内存分配,使用unsafe.allocateMemory()申请分配内存。不足时会出现OutOfMemoryError。
二.垃圾收集器与内存分配策略
概述
Java内存运行时区域的各个部分,其中程序计数器、VM栈、本地方法栈三个区域随线程而生,随线程而灭;栈中的帧随着方法进入、退出而有条不紊的进行着出栈入栈操作。而Java堆和方法区(包括运行时常量池)则不一样,我们必须等到程序实际运行期间才能知道会创建哪些对象,这部分内存的分配和回收都是动态的。
判断对象已死
1)引用计数算法(对象中添加一个引用计数器,当有一个地方引用它,计数器加1,当引用失效,计数器减1,任何时刻计数器为0的对象就是不可能再被使用的),但引用计数算法无法解决对象循环引用的问题。
根搜索算法(通过一系列的称为“GCRoots”的点作为起始进行向下搜索,当一个对象到GCRoots没有任何引用链(ReferenceChain)相连,则证明此对象是不可用的),主流程序语言Java,c#都使用此算法。在Java语言中,GC Roots包括:
1.在VM栈(帧中的本地变量)中的引用。2.方法区中的静态引用和常量引用的对象。3.JNI(即一般说的Native方法)中的引用。
2)生存还是死亡?
判定一个对象死亡,至少经历两次标记过程:如果对象在进行根搜索后,发现没有与GC Roots相连接的引用链,那它将会被第一次标记,并在稍后执行他的finalize()方法(如果它有的话)。这里所谓的“执行”是指虚拟机会触发这个方法,但并不承诺会等待它运行结束。这点是必须的,否则一个对象在finalize()方法执行缓慢,甚至有死循环什么的将会很容易导致整个系统崩溃。 finalize()方法是对象最后一次逃脱死亡命运的机会,稍后GC将进行第二次规模稍小的标记,如果在finalize()中对象成功拯救自己(只要重新建立到GC Roots的连接即可,譬如把自己赋值到某个引用上),那在第二次标记时它将被移除出“即将回收”的集合,如果对象这时候还没有逃脱,那基本上它就真的离死不远了。需要关闭外部资源之类的事情,基本上它能做的使用try-finally可以做的更好。
3)回收方法区
方法区即后文提到的永久代,很多人认为永久代是没有GC的,这区GC的“性价比”一般比较低:在堆中,尤其是在新生代,进行一次GC可以一般可以回收70%~95%的空间,而永久代的GC效率远小于此。但是目前方法区主要回收两部分内容:废弃常量与无用类。需要满足下面3个条件:1.该类所有的实例都已经被GC,也就是JVM中不存在该Class的任何实例。2.加载该类的ClassLoader已经被GC。3.该类对应的java.lang.Class对象没有在任何地方被引用,如不能在任何地方通过反射访问该类的方法。
垃圾收集算法
1.标记-清除算法(Mark-Sweep)
算法分成“标记”和“清除”两个阶段,首先标记出所有需要回收的对象,然后回收所有需要回收的对象。主要缺点有两个,一是效率问题,标记和清理两个过程效率都不高,二是空间问题,标记清理之后会产生大量不连续的内存碎片,空间碎片太多可能会导致后续使用中无法找到足够的连续内存而提前触发另一次的垃圾搜集动作。
2.复制算法(Copying)
将内存分为一块较大的eden空间和2块较少的survivor空间,每次使用eden和其中一块survivor,当回收时将eden和 survivor还存活的对象一次过拷贝到另外一块survivor空间上,然后清理掉eden和用过的survivor。复制收集算法在对象存活率高的时候,效率有所下降。
3.标记-整理(Mark-Compact)算法
标记过程仍然一样,但后续步骤不是进行直接清理,而是令所有存活的对象一端移动,然后直接清理掉这端边界以外的内存。
4.分代收集(Generational Collection)算法
此算法只是根据对象不同的存活周期将内存划分为几块。一般是把Java堆分作新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。
垃圾收集器
没有最好的收集器,也没有万能的收集器,只有最合适的收集器。
1.Serial收集器单线程收集器,收集时会暂停所有工作线程(我们将这件事情称之为Stop The World,下称STW),使用复制收集算法,虚拟机运行在Client模式时的默认新生代收集器。2.ParNew收集器ParNew收集器就是Serial的多线程版本,除了使用多条收集线程外,其余行为包括算法、STW、对象分配规则、回收策略等都与Serial收集器一摸一样。对应的这种收集器是虚拟机运行在Server模式的默认新生代收集器,在单CPU的环境中,ParNew收集器并不会比Serial收集器有更好的效果。3.Parallel Scavenge收集器Parallel Scavenge收集器(下称PS收集器)也是一个多线程收集器,也是使用复制算法,但它的对象分配规则与回收策略都与ParNew收集器有所不同,它是以吞吐量最大化(即GC时间占总运行时间最小)为目标的收集器实现,它允许较长时间的STW换取总吞吐量最大化。4.Serial Old收集器Serial Old是单线程收集器,使用标记-整理算法,是老年代的收集器,上面三种都是使用在新生代收集器。5.Parallel Old收集器老年代版本吞吐量优先收集器,使用多线程和标记-整理算法,JVM 1.6提供,在此之前,新生代使用了PS收集器的话,老年代除Serial Old外别无选择,因为PS无法与CMS收集器配合工作。6.CMS(Concurrent Mark Sweep)收集器CMS是一种以最短停顿时间为目标的收集器,使用CMS并不能达到GC效率最高(总体GC时间最小),但它能尽可能降低GC时服务的停顿时间,这一点对于实时或者高交互性应用(譬如证券交易)来说至关重要,这类应用对于长时间STW一般是不可容忍的。CMS收集器使用的是标记-清除算法,也就是说它在运行期间会产生空间碎片,所以虚拟机提供了参数开启CMS收集结束后再进行一次内存压缩。
⑤ 如何使用Unsafe操作内存中的Java类和对象
本文由ImportNew-吴际翻译自zeroturnaround。欢迎加入翻译小组。转载请参见文章末尾的要求。让我们开始展示内存中Java类和对象结构你可曾好奇过Java内存管理核心构件?你是否问过自己某些奇怪的问题,比如:一个类在内存中占据多少空间?我的对象在内存中消耗了多少空间?对象的属性在内存中是如何被布局的?如果这些问题听起来很熟悉,那么你就想到了点子上。对于像我们这样的在RebelLabs的Java极客来说,这些难解的谜题已经在我们脑海中缠绕了很长时间:如果你对探究类检测器感兴趣,想知道如何布局让所有的类更容易地从内存中取到指定变量,或是想在系统运行时侵入内存中的这些字段。这就意味着你能切实改变内存中的数据甚至是代码!其它可能勾起你兴趣的知识点有,“堆外缓存”和“高性能序列化”的实现。这是一对构建在对象缓存结构上很好的实例,揭示了获取类和实例内存地址的方法,缓存中类和实例的布局以及关于对象成员变量布局的详细解释。我们希望尽可能简单地阐释这些内容,但是尽管如此这篇文章并不适合Java初学者,它要求具备对Java编程原理有一定的了解。注意:下面关于类和对象的布局所写的内容特指JavaSE7,所以不推荐使用者想当然地认为这些适用于过去或将来的Java版本。方便起见,我们在GitHub项目上发布了这篇文章的示例代码,可以在这里找到。在Java中最直接的内存操作方法是什么?Java最初被设计为一种安全的受控环境。尽管如此,JavaHotSpot还是包含了一个“后门”,提供了一些可以直接操控内存和线程的低层次操作。这个后门类——sun.misc.Unsafe——被JDK广泛用于自己的包中,如java.nio和java.util.concurrent。但是丝毫不建议在生产环境中使用这个后门。因为这个API十分不安全、不轻便、而且不稳定。这个不安全的类提供了一个观察HotSpotJVM内部结构并且可以对其进行修改。有时它可以被用来在不适用C++调试的情况下学习虚拟机内部结构,有时也可以被拿来做性能监控和开发工具。为何变得不安全sun.misc.Unsafe这个类是如此地不安全,以至于JDK开发者增加了很多特殊限制来访问它。它的构造器是私有的,工厂方法getUnsafe()的调用器只能被Bootloader加载。如你在下面代码片段的第8行所见,这个家伙甚至没有被任何类加载器加载,所以它的类加载器是null。它会抛出SecurityException异常来阻止侵入者。publicfinalclassUnsafe{privateUnsafe(){}=newUnsafe();publicstaticUnsafegetUnsafe(){Classcc=sun.reflect.Reflection.getCallerClass(2);if(cc.getClassLoader()!=null)thrownewSecurityException("Unsafe");returntheUnsafe;}}幸运的是这里有一个Unsafe的变量可以被用来取得Unsafe的实例。我们可以轻松地编写一个复制方法通过反射来实现,如下所示:()publicstaticUnsafegetUnsafe(){try{Fieldf=Unsafe.class.getDeclaredField("theUnsafe");f.setAccessible(true);return(Unsafe)f.get(null);}catch(Exceptione){/**/}}Unsafe一些有用的特性虚拟机“集约化”(VMintrinsification):如用于无锁Hash表中的CAS(比较和交换)。再比如compareAndSwapInt这个方法用JNI调用,包含了对CAS有特殊引导的本地代码。在这里你能读到关于CAS的信息:。主机虚拟机(译注:主机虚拟机主要用来管理其他虚拟机。而虚拟平台我们看到只有guestVM)的sun.misc.Unsafe功能能够被用于未初始化的对象分配内存(用allocateInstance方法),然后将构造器调用解释为其他方法的调用。你可以从本地内存地址中追踪到这些数据。使用java.lang.Unsafe类获取内存地址是可能的。而且可以通过unsafe方法直接操作这些变量!使用allocateMemory方法,内存可以被分配到堆外。例如当allocateDirect方法被调用时DirectByteBuffer构造器内部会使用allocateMemory。arrayBaseOffset和arrayIndexScale方法可以被用于开发arraylets,一种用来将大数组分解为小对象、限制扫描的实时消耗或者在大对象上做更新和移动。
⑥ C#代码unsafe public static extern string shibie1(byte* ptr, int w, int h);转成java代码应该怎么转
你肯定是C# 代码,C#有指针了??
PS:unsafe 的 还有地址操作,还是按功能重新吧。否则,不涉及地址操作,直接按代码用 java 类重写。
⑦ unsafe java 源码怎么查看
sun.misc.Unsafe至少从2004年Java1.4开始就存在于Java中了。在Java9中,为了提高JVM的可维护性,Unsafe和许多其他的东西一起都被作为内部使用类隐藏起来了。但是究竟是什么取代Unsafe不得而知,个人推测会有不止一样来取代它
⑧ java学习,根据要求创建相同(存储地址相同)的对象
第一步骤:解决方式直接复制就可以了。
publicclassTest6{
publicstaticvoidmain(String[]args){
Personperson1=newPerson(1,"Tom",21);
Personperson2=person1;
System.out.println(person1.hashCode()+""+person2.hashCode());
System.out.println(person1.equals(person2));
System.out.println(person2==person1);
}
}
⑨ 如何获得java对象的内存地址
在java中内存中的对象地址是可变的,所以获得的内存地址有可能会变化。要获得内存地址也只能通过Unsafe的方法来获得,如下代码示例:
packagecom.bijian.study;
importjava.lang.reflect.Field;
importsun.misc.Unsafe;
publicclassAddresser{
//实例化Unsafe 类
privatestaticUnsafeunsafe;
static{
try{
//得到field对象
Fieldfield=Unsafe.class.getDeclaredField("theUnsafe");
//设置获取地址
field.setAccessible(true);
unsafe=(Unsafe)field.get(null);
}catch(Exceptione){
e.printStackTrace();
}
}
publicstaticlongaddressOf(Objecto)throwsException{
Object[]array=newObject[]{o};
longbaseOffset=unsafe.arrayBaseOffset(Object[].class);
intaddressSize=unsafe.addressSize();
longobjectAddress;
switch(addressSize){
case4:
objectAddress=unsafe.getInt(array,baseOffset);
break;
case8:
objectAddress=unsafe.getLong(array,baseOffset);
break;
default:
thrownewError("unsupportedaddresssize:"+addressSize);
}
return(objectAddress);
}
//打印地址的长度
publicstaticvoidmain(String...args)throwsException{
Objectmine="Hithere".toCharArray();
longaddress=addressOf(mine);
System.out.println("Addess:"+address);
//Verifyaddressworks-
printBytes(address,27);
}
//调用此方法得到地址
publicstaticvoidprintBytes(longobjectAddress,intnum){
//循环打印得到的地址。
for(longi=0;i<num;i++){
intcur=unsafe.getByte(objectAddress+i);
System.out.print((char)cur);
}
System.out.println();
}
}
运行结果:
⑩ Java为什么会引入及如何使用Unsafe
sun.misc.Unsafe类提供了很多Native本地方法,通过这个类你可以直接进行内存,cpu相关的底层调用操作:
例如:
通过allocateInstance()方法,你可以创建一个类的实例,但是却不需要调用它的构造函数、初使化代码、各种JVM安全检查以及其它的一些底层的东西。
使用objectFieldOffset方法我们可以实现C语言式的sizeOf功能。这个实现返回了对象的浅大小(shallow size )。
使用Unsafe. compareAndSwap*方法可以通过调用C++程序实现cpu级别的同步。(CAS)
java中的java.util.concurrent包都是通过Unsafe的compareAndSwap*方法实现的。
但这个类默认是不被允许创建实例的:
1 构造函数私有。 2 只有JDK信任的类才可以调用Unsafe.getUnsafe() 方法获取实例。
虽然正常情况下无法调用,但我们可以通过反射来调用。
Fieldf=Unsafe.class.getDeclaredField(theUnsafe);
f.setAccessible(true);
Unsafeunsafe=(Unsafe)f.get(null);
这样Eclipse会报错,但是这样就可以直接运行了。