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,請謹慎使用。