Unsafe类在jdk 源码的多个类中用到,这个类的提供了一些绕开JVM的更底层功能,基于它的实现能够提升效率。可是,它是一把双刃剑:正如它的名字所预示的那样,它是Unsafe的,它所分配的内存须要手动free(不被GC回收)。Unsafe类,提供了JNI某些功能的简单替代:确保高效性的同时,使事情变得更简单。html
这篇文章主要是如下文章的整理、翻译。java
http://mishadoff.com/blog/java-magic-part-4-sun-dot-misc-dot-unsafe/linux
1. Unsafe API的大部分方法都是native实现,它由105个方法组成,主要包括如下几类:windows
(1)Info相关。主要返回某些低级别的内存信息:addressSize(), pageSize()数组
(2)Objects相关。主要提供Object和它的域操纵方法:allocateInstance(),objectFieldOffset()并发
(3)Class相关。主要提供Class和它的静态域操纵方法:staticFieldOffset(),defineClass(),defineAnonymousClass(),ensureClassInitialized()框架
(4)Arrays相关。数组操纵方法:arrayBaseOffset(),arrayIndexScale()jvm
(5)Synchronization相关。主要提供低级别同步原语(如基于CPU的CAS(Compare-And-Swap)原语):monitorEnter(),tryMonitorEnter(),monitorExit(),compareAndSwapInt(),putOrderedInt()函数
(6)Memory相关。直接内存访问方法(绕过JVM堆直接操纵本地内存):allocateMemory(),copyMemory(),freeMemory(),getAddress(),getInt(),putInt()ui
正常状况下无法实例化一个私有构造函数的类,可是Unsafe能够作到。
import java.lang.reflect.Field; import sun.misc.Unsafe; public class UnsafePlayer { public static void main(String[] args) throws Exception { //经过反射实例化Unsafe Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); Unsafe unsafe = (Unsafe) f.get(null); //实例化Player Player player = (Player) unsafe.allocateInstance(Player.class); player.setName("li lei"); System.out.println(player.getName()); } } class Player{ private String name; private Player(){} public String getName() { return name; } public void setName(String name) { this.name = name; } }
import java.lang.reflect.Field; import sun.misc.Unsafe; public class UnsafePlayer { public static void main(String[] args) throws Exception { //经过反射实例化Unsafe Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); Unsafe unsafe = (Unsafe) f.get(null); //实例化Player Player player = (Player) unsafe.allocateInstance(Player.class); player.setAge(18); player.setName("li lei"); for(Field field:Player.class.getDeclaredFields()){ System.out.println(field.getName()+":对应的内存偏移地址"+unsafe.objectFieldOffset(field)); } System.out.println("-------------------"); int ageOffset= 8; //修改内存偏移地址为8的值(age),返回true,说明经过内存偏移地址修改age的值成功 System.out.println(unsafe.compareAndSwapInt(player, ageOffset, 18, 20)); System.out.println("age修改后的值:"+player.getAge()); System.out.println("-------------------"); //修改内存偏移地址为8的值,可是修改后不保证立马能被其余的线程看到。 unsafe.putOrderedInt(player, 8, 33); System.out.println("age修改后的值:"+player.getAge()); System.out.println("-------------------"); //修改内存偏移地址为12的值,volatile修饰,修改能立马对其余线程可见 unsafe.putObjectVolatile(player, 12, "han mei"); System.out.println("name修改后的值:"+unsafe.getObjectVolatile(player, 12)); } } class Player{ private int age; private String name; private Player(){} public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
java中数组的最大长度为Integer.MAX_VALUE,正常状况下若是想建立一个大于Integer.MAX_VALUE的数组是作不到的,可是Unsafe能够,经过对内存进行直接分配实现。
import java.lang.reflect.Field; import sun.misc.Unsafe; public class SuperArray { public static void main(String[] arg) throws Exception{ //经过反射实例化Unsafe Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); Unsafe unsafe = (Unsafe) f.get(null); //只要内存够大,能够把这个调大,大于Integer.MAX_VALUE long size = (long)Integer.MAX_VALUE/2 ; long addr = unsafe.allocateMemory(size); System.out.println("unsafe address :"+addr); for (int i = 0; i < size; i++) { unsafe.putByte(addr+i, (byte)6); if(unsafe.getByte(addr+i) !=6){ System.out.println("failed at offset"); } } } }
把size调大,size = (long)Integer.MAX_VALUE*2,错误信息以下。
unsafe address :15817328 # # A fatal error has been detected by the Java Runtime Environment: # # EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x61cc0350, pid=31240, tid=31324 # # JRE version: Java(TM) SE Runtime Environment (8.0_45-b14) (build 1.8.0_45-b14) # Java VM: Java HotSpot(TM) Client VM (25.45-b02 mixed mode windows-x86 ) # Problematic frame: # V[thread 30484 also had an error] [jvm.dll+0x40350] # # Failed to write core dump. Minidumps are not enabled by default on client versions of Windows # # An error report file with more information is saved as: # C:\welab\workspace\Person\hs_err_pid31240.log
将一个线程进行挂起是经过park方法实现的,调用 park后,线程将一直阻塞直到超时或者中断等条件出现。unpark能够终止一个挂起的线程,使其恢复正常。整个并发框架中对线程的挂起操做被封装在 LockSupport类中,LockSupport类中有各类版本pack方法,但最终都调用了Unsafe.park()方法。
public class LockSupport { /** * 恢复阻塞线程 */ public static void unpark(Thread thread) { if (thread != null) unsafe.unpark(thread); } /** * 一直阻塞当前线程,调用Unsafe.park()方法 */ public static void park(Object blocker) { Thread t = Thread.currentThread(); setBlocker(t, blocker); unsafe.park(false, 0L); setBlocker(t, null); } /** * 阻塞当前线程nanos纳秒 */ public static void parkNanos(Object blocker, long nanos) { if (nanos > 0) { Thread t = Thread.currentThread(); setBlocker(t, blocker); unsafe.park(false, nanos); setBlocker(t, null); } } public static void parkUntil(Object blocker, long deadline) { Thread t = Thread.currentThread(); setBlocker(t, blocker); unsafe.park(true, deadline); setBlocker(t, null); } /** * 一直阻塞当前线程 */ public static void park() { unsafe.park(false, 0L); } /** * 阻塞当前线程nanos纳秒 */ public static void parkNanos(long nanos) { if (nanos > 0) unsafe.park(false, nanos); } public static void parkUntil(long deadline) { unsafe.park(true, deadline); } }
最后看下阻塞和恢复的例子
import java.util.concurrent.locks.LockSupport; public class Lock { public static void main(String[] args) throws InterruptedException { ThreadPark threadPark = new ThreadPark(); threadPark.start(); ThreadUnPark threadUnPark = new ThreadUnPark(threadPark); threadUnPark.start(); //等待threadUnPark执行成功 threadUnPark.join(); System.out.println("运行成功...."); } static class ThreadPark extends Thread{ public void run(){ System.out.println(Thread.currentThread() +"我将被阻塞在这了60s...."); //阻塞60s,单位纳秒 1s = 1000000000 LockSupport.parkNanos(1000000000l*60); System.out.println(Thread.currentThread() +"我被恢复正常了...."); } } static class ThreadUnPark extends Thread{ public Thread thread = null; public ThreadUnPark(Thread thread){ this.thread = thread; } public void run(){ System.out.println("提早恢复阻塞线程ThreadPark"); //恢复阻塞线程 LockSupport.unpark(thread); } } }
参考:https://blog.csdn.net/dfdsggdgg/article/details/51543545
http://blog.csdn.net/fenglibing/article/details/17138079
https://www.cnblogs.com/suxuan/p/4948608.html
Java 9中将移除 Sun.misc.Unsafe,请谨慎使用。