前言
本文主要給大家介紹了關于Java中Unsafe類的相關內容,分享出來供大家參考學習,下面話不多說了,來一起看看詳細的介紹吧
1.Unsafe類介紹
Unsafe類是在sun.misc包下,不屬于Java標準。但是很多Java的基礎類庫,包括一些被廣泛使用的高性能開發庫都是基于Unsafe類開發的,比如Netty、Hadoop、Kafka等。
使用Unsafe可用來直接訪問系統內存資源并進行自主管理,Unsafe類在提升Java運行效率,增強Java語言底層操作能力方面起了很大的作用。
Unsafe可認為是Java中留下的后門,提供了一些低層次操作,如直接內存訪問、線程調度等。
官方并不建議使用Unsafe。
下面是使用Unsafe的一些例子。
1.1實例化私有類
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; } }
1.2CAS操作,通過內存偏移地址修改變量值
java并發包中的SynchronousQueue中的TransferStack中使用CAS更新棧頂。
/ Unsafe mechanicsprivate static final sun.misc.Unsafe UNSAFE;private static final long headOffset;static { try { UNSAFE = sun.misc.Unsafe.getUnsafe(); Class<?> k = TransferStack.class; headOffset = UNSAFE.objectFieldOffset (k.getDeclaredField("head")); } catch (Exception e) { throw new Error(e); }}//棧頂volatile SNode head;//更新棧頂boolean casHead(SNode h, SNode nh) { return h == head && UNSAFE.compareAndSwapObject(this, headOffset, h, nh);}
1.3直接內存訪問
Unsafe的直接內存訪問:用Unsafe開辟的內存空間不占用Heap空間,當然也不具有自動內存回收功能。做到像C一樣自由利用系統內存資源。
2.Unsafe類源碼分析
Unsafe的大部分API都是native的方法,主要包括以下幾類:
1)Class相關。主要提供Class和它的靜態字段的操作方法。
2)Object相關。主要提供Object和它的字段的操作方法。
3)Arrray相關。主要提供數組及其中元素的操作方法。
4)并發相關。主要提供低級別同步原語,如CAS、線程調度、volatile、內存屏障等。
5)Memory相關。提供了直接內存訪問方法(繞過Java堆直接操作本地內存),可做到像C一樣自由利用系統內存資源。
6)系統相關。主要返回某些低級別的內存信息,如地址大小、內存頁大小。
2.1Class相關
//靜態屬性的偏移量,用于在對應的Class對象中讀寫靜態屬性public native long staticFieldOffset(Field f); public native Object staticFieldBase(Field f);//判斷是否需要初始化一個類public native boolean shouldBeInitialized(Class<?> c);//確保類被初始化public native void ensureClassInitialized(Class<?> c);//定義一個類,可用于動態創建類public native Class<?> defineClass(String name, byte[] b, int off, int len, ClassLoader loader, ProtectionDomain protectionDomain);//定義一個匿名類,可用于動態創建類public native Class<?> defineAnonymousClass(Class<?> hostClass, byte[] data, Object[] cpPatches);
2.2Object相關
Java中的基本類型(boolean、byte、char、short、int、long、float、double)及對象引用類型都有以下方法。
//獲得對象的字段偏移量 public native long objectFieldOffset(Field f); //獲得給定對象地址偏移量的int值public native int getInt(Object o, long offset);//設置給定對象地址偏移量的int值public native void putInt(Object o, long offset, int x);
//創建對象,但并不會調用其構造方法。如果類未被初始化,將初始化類。public native Object allocateInstance(Class<?> cls) throws InstantiationException;
2.3數組相關
/** * Report the offset of the first element in the storage allocation of a * given array class. If {@link #arrayIndexScale} returns a non-zero value * for the same class, you may use that scale factor, together with this * base offset, to form new offsets to access elements of arrays of the * given class. * * @see #getInt(Object, long) * @see #putInt(Object, long, int) *///返回數組中第一個元素的偏移地址public native int arrayBaseOffset(Class<?> arrayClass);//boolean、byte、short、char、int、long、float、double,及對象類型均有以下方法/** The value of {@code arrayBaseOffset(boolean[].class)} */public static final int ARRAY_BOOLEAN_BASE_OFFSET = theUnsafe.arrayBaseOffset(boolean[].class); /** * Report the scale factor for addressing elements in the storage * allocation of a given array class. However, arrays of "narrow" types * will generally not work properly with accessors like {@link * #getByte(Object, int)}, so the scale factor for such classes is reported * as zero. * * @see #arrayBaseOffset * @see #getInt(Object, long) * @see #putInt(Object, long, int) *///返回數組中每一個元素占用的大小public native int arrayIndexScale(Class<?> arrayClass); //boolean、byte、short、char、int、long、float、double,及對象類型均有以下方法/** The value of {@code arrayIndexScale(boolean[].class)} */public static final int ARRAY_BOOLEAN_INDEX_SCALE = theUnsafe.arrayIndexScale(boolean[].class);
通過arrayBaseOffset和arrayIndexScale可定位數組中每個元素在內存中的位置。
2.4并發相關
2.4.1CAS相關
CAS:CompareAndSwap,內存偏移地址offset,預期值expected,新值x。如果變量在當前時刻的值和預期值expected相等,嘗試將變量的值更新為x。如果更新成功,返回true;否則,返回false。
//更新變量值為x,如果當前值為expected//o:對象 offset:偏移量 expected:期望值 x:新值public final native boolean compareAndSwapObject(Object o, long offset, Object expected, Object x); public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x); public final native boolean compareAndSwapLong(Object o, long offset, long expected, long x);
從Java 8開始,Unsafe中提供了以下方法:
//增加public final int getAndAddInt(Object o, long offset, int delta) { int v; do { v = getIntVolatile(o, offset); } while (!compareAndSwapInt(o, offset, v, v + delta)); return v;} public final long getAndAddLong(Object o, long offset, long delta) { long v; do { v = getLongVolatile(o, offset); } while (!compareAndSwapLong(o, offset, v, v + delta)); return v;}//設置public final int getAndSetInt(Object o, long offset, int newValue) { int v; do { v = getIntVolatile(o, offset); } while (!compareAndSwapInt(o, offset, v, newValue)); return v;} public final long getAndSetLong(Object o, long offset, long newValue) { long v; do { v = getLongVolatile(o, offset); } while (!compareAndSwapLong(o, offset, v, newValue)); return v;} public final Object getAndSetObject(Object o, long offset, Object newValue) { Object v; do { v = getObjectVolatile(o, offset); } while (!compareAndSwapObject(o, offset, v, newValue)); return v;
2.4.2線程調度相關
//取消阻塞線程public native void unpark(Object thread);//阻塞線程public native void park(boolean isAbsolute, long time);//獲得對象鎖public native void monitorEnter(Object o);//釋放對象鎖public native void monitorExit(Object o);//嘗試獲取對象鎖,返回true或false表示是否獲取成功public native boolean tryMonitorEnter(Object o);
2.4.3volatile相關讀寫
Java中的基本類型(boolean、byte、char、short、int、long、float、double)及對象引用類型都有以下方法。
//從對象的指定偏移量處獲取變量的引用,使用volatile的加載語義//相當于getObject(Object, long)的volatile版本public native Object getObjectVolatile(Object o, long offset); //存儲變量的引用到對象的指定的偏移量處,使用volatile的存儲語義//相當于putObject(Object, long, Object)的volatile版本public native void putObjectVolatile(Object o, long offset, Object x);
/** * Version of {@link #putObjectVolatile(Object, long, Object)} * that does not guarantee immediate visibility of the store to * other threads. This method is generally only useful if the * underlying field is a Java volatile (or if an array cell, one * that is otherwise only accessed using volatile accesses). */public native void putOrderedObject(Object o, long offset, Object x); /** Ordered/Lazy version of {@link #putIntVolatile(Object, long, int)} */public native void putOrderedInt(Object o, long offset, int x); /** Ordered/Lazy version of {@link #putLongVolatile(Object, long, long)} */public native void putOrderedLong(Object o, long offset, long x);
2.4.4內存屏障相關
Java 8引入 ,用于定義內存屏障,避免代碼重排序。
//內存屏障,禁止load操作重排序,即屏障前的load操作不能被重排序到屏障后,屏障后的load操作不能被重排序到屏障前public native void loadFence();//內存屏障,禁止store操作重排序,即屏障前的store操作不能被重排序到屏障后,屏障后的store操作不能被重排序到屏障前public native void storeFence();//內存屏障,禁止load、store操作重排序public native void fullFence();
2.5直接內存訪問(非堆內存)
allocateMemory所分配的內存需要手動free(不被GC回收)
//(boolean、byte、char、short、int、long、float、double)都有以下get、put兩個方法。 //獲得給定地址上的int值public native int getInt(long address);//設置給定地址上的int值public native void putInt(long address, int x);//獲得本地指針public native long getAddress(long address);//存儲本地指針到給定的內存地址public native void putAddress(long address, long x); //分配內存public native long allocateMemory(long bytes);//重新分配內存public native long reallocateMemory(long address, long bytes);//初始化內存內容public native void setMemory(Object o, long offset, long bytes, byte value);//初始化內存內容public void setMemory(long address, long bytes, byte value) { setMemory(null, address, bytes, value);}//內存內容拷貝public native void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes);//內存內容拷貝public void copyMemory(long srcAddress, long destAddress, long bytes) { copyMemory(null, srcAddress, null, destAddress, bytes);}//釋放內存public native void freeMemory(long address);
2.6系統相關
//返回指針的大小。返回值為4或8。public native int addressSize(); /** The value of {@code addressSize()} */public static final int ADDRESS_SIZE = theUnsafe.addressSize(); //內存頁的大小。public native int pageSize();
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VeVb武林網的支持。
新聞熱點
疑難解答
圖片精選