java中Unsafe的使用講解
前段時間因為看JUC的源碼,里面有大量關(guān)于unsafe的操作,所以就來看看了.寫點筆記總結(jié)下(本文基于jdk1.8):
unsafe可以幫我們直接去操作硬件資源,當(dāng)然了是借助java的jit來進行的,官方不推薦使用,因為不安全,例如你使用unsafe創(chuàng)建一個超級大的數(shù)組,但是這個數(shù)組jvm是不管理的,只能你自己操作,容易oom,也不利于資源的回收。
好了,下面我們來看代碼
1.獲取unsafe
//1.最簡單的使用方式是基于反射獲取Unsafe實例 Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); Unsafe unsafe = (Unsafe) f.get(null);
2.獲取unsafe
private static Unsafe unsafe = null; private static Field getUnsafe = null; static { try { getUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); getUnsafe.setAccessible(true); unsafe = (Unsafe) getUnsafe.get(null); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } }
隨便只要你高興,都可以獲取到unfase,因為涉及到unfase 的權(quán)限問題,所以,我們只能使用這種方式獲取,不然就是權(quán)限異常,
操作方法:
/** * 操作數(shù)組: * 可以獲取數(shù)組的在內(nèi)容中的基本偏移量(arrayBaseOffset),獲取數(shù)組內(nèi)元素的間隔(比例), * 根據(jù)數(shù)組對象和偏移量獲取元素值(getObject),設(shè)置數(shù)組元素值(putObject),示例如下。 */ String[] strings = new String[]{"1", "2", "3"}; long i = unsafe.arrayBaseOffset(String[].class); System.out.println("string[] base offset is :" + i); //every index scale long scale = unsafe.arrayIndexScale(String[].class); System.out.println("string[] index scale is " + scale); //print first string in strings[] System.out.println("first element is :" + unsafe.getObject(strings, i)); //set 100 to first string unsafe.putObject(strings, i + scale * 0, "100"); //print first string in strings[] again System.out.println("after set ,first element is :" + unsafe.getObject(strings, i + scale * 0));
/** * 對象操作 * 實例化Data * * 可以通過類的class對象創(chuàng)建類對象(allocateInstance),獲取對象屬性的偏移量(objectFieldOffset) * ,通過偏移量設(shè)置對象的值(putObject) * * 對象的反序列化 * 當(dāng)使用框架反序列化或者構(gòu)建對象時,會假設(shè)從已存在的對象中重建,你期望使用反射來調(diào)用類的設(shè)置函數(shù), * 或者更準(zhǔn)確一點是能直接設(shè)置內(nèi)部字段甚至是final字段的函數(shù)。問題是你想創(chuàng)建一個對象的實例, * 但你實際上又不需要構(gòu)造函數(shù),因為它可能會使問題更加困難而且會有副作用。 * */ //調(diào)用allocateInstance函數(shù)避免了在我們不需要構(gòu)造函數(shù)的時候卻調(diào)用它 Data data = (Data) unsafe.allocateInstance(Data.class); data.setId(1L); data.setName("unsafe"); System.out.println(data); //返回成員屬性在內(nèi)存中的地址相對于對象內(nèi)存地址的偏移量 Field nameField = Data.class.getDeclaredField("name"); long fieldOffset = unsafe.objectFieldOffset(nameField); //putLong,putInt,putDouble,putChar,putObject等方法,直接修改內(nèi)存數(shù)據(jù)(可以越過訪問權(quán)限) unsafe.putObject(data,fieldOffset,"這是新的值"); System.out.println(data.getName()); /** * 我們可以在運行時創(chuàng)建一個類,比如從已編譯的.class文件中。將類內(nèi)容讀取為字節(jié)數(shù)組, * 并正確地傳遞給defineClass方法;當(dāng)你必須動態(tài)創(chuàng)建類,而現(xiàn)有代碼中有一些代理, 這是很有用的 */ File file = new File("C:\\workspace\\idea2\\disruptor\\target\\classes\\com\\onyx\\distruptor\\test\\Data.class"); FileInputStream input = new FileInputStream(file); byte[] content = new byte[(int)file.length()]; input.read(content); Class c = unsafe.defineClass(null, content, 0, content.length,null,null); c.getMethod("getId").invoke(c.newInstance(), null); /** * 內(nèi)存操作 * 可以在Java內(nèi)存區(qū)域中分配內(nèi)存(allocateMemory),設(shè)置內(nèi)存(setMemory,用于初始化), * 在指定的內(nèi)存位置中設(shè)置值(putInt\putBoolean\putDouble等基本類型) */ //分配一個8byte的內(nèi)存 long address = unsafe.allocateMemory(8L); //初始化內(nèi)存填充1 unsafe.setMemory(address, 8L, (byte) 1); //測試輸出 System.out.println("add byte to memory:" + unsafe.getInt(address)); //設(shè)置0-3 4個byte為0x7fffffff unsafe.putInt(address, 0x7fffffff); //設(shè)置4-7 4個byte為0x80000000 unsafe.putInt(address + 4, 0x80000000); //int占用4byte System.out.println("add byte to memory:" + unsafe.getInt(address)); System.out.println("add byte to memory:" + unsafe.getInt(address + 4));
/** * CAS操作 * Compare And Swap(比較并交換),當(dāng)需要改變的值為期望的值時,那么就替換它為新的值,是原子 * (不可在分割)的操作。很多并發(fā)框架底層都用到了CAS操作,CAS操作優(yōu)勢是無鎖,可以減少線程切換耗費 * 的時間,但CAS經(jīng)常失敗運行容易引起性能問題,也存在ABA問題。在Unsafe中包含compareAndSwapObject、 * compareAndSwapInt、compareAndSwapLong三個方法,compareAndSwapInt的簡單示例如下。 */ Data data = new Data(); data.setId(1L); Field id = data.getClass().getDeclaredField("id"); long l = unsafe.objectFieldOffset(id); id.setAccessible(true); //比較并交換,比如id的值如果是所期望的值1,那么就替換為2,否則不做處理 unsafe.compareAndSwapLong(data,1L,1L,2L); System.out.println(data.getId());
/** * 常量獲取 * * 可以獲取地址大小(addressSize),頁大小(pageSize),基本類型數(shù)組的偏移量 * (Unsafe.ARRAY_INT_BASE_OFFSET\Unsafe.ARRAY_BOOLEAN_BASE_OFFSET等)、 * 基本類型數(shù)組內(nèi)元素的間隔(Unsafe.ARRAY_INT_INDEX_SCALE\Unsafe.ARRAY_BOOLEAN_INDEX_SCALE等) */ //get os address size System.out.println("address size is :" + unsafe.addressSize()); //get os page size System.out.println("page size is :" + unsafe.pageSize()); //int array base offset System.out.println("unsafe array int base offset:" + Unsafe.ARRAY_INT_BASE_OFFSET); /** * 線程許可 * 許可線程通過(park),或者讓線程等待許可(unpark), */ Thread packThread = new Thread(() -> { long startTime = System.currentTimeMillis(); //納秒,相對時間park unsafe.park(false,3000000000L); //毫秒,絕對時間park //unsafe.park(true,System.currentTimeMillis()+3000); System.out.println("main thread end,cost :"+(System.currentTimeMillis()-startTime)+"ms"); }); packThread.start(); TimeUnit.SECONDS.sleep(1); //注釋掉下一行后,線程3秒數(shù)后進行輸出,否則在1秒后輸出 unsafe.unpark(packThread);
/** * Java數(shù)組大小的最大值為Integer.MAX_VALUE。使用直接內(nèi)存分配,我們創(chuàng)建的數(shù)組大小受限于堆大?。? * 實際上,這是堆外內(nèi)存(off-heap memory)技術(shù),在java.nio包中部分可用; * * 這種方式的內(nèi)存分配不在堆上,且不受GC管理,所以必須小心Unsafe.freeMemory()的使用。 * 它也不執(zhí)行任何邊界檢查,所以任何非法訪問可能會導(dǎo)致JVM崩潰 */ public class SuperArray { private static Unsafe unsafe = null; private static Field getUnsafe = null; static { try { getUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); getUnsafe.setAccessible(true); unsafe = (Unsafe) getUnsafe.get(null); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } private final static int BYTE = 1; private long size; private long address; public SuperArray(long size) { this.size = size; address = unsafe.allocateMemory(size * BYTE); } public void set(long i, byte value) { unsafe.putByte(address + i * BYTE, value); } public int get(long idx) { return unsafe.getByte(address + idx * BYTE); } public long size() { return size; } public static void main(String[] args) { long SUPER_SIZE = (long)Integer.MAX_VALUE * 2; SuperArray array = new SuperArray(SUPER_SIZE); System.out.println("Array size:" + array.size()); // 4294967294 int sum=0; for (int i = 0; i < 100; i++) { array.set((long)Integer.MAX_VALUE + i, (byte)3); sum += array.get((long)Integer.MAX_VALUE + i); } System.out.println(sum); } }
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
詳解Guava Cache本地緩存在Spring Boot應(yīng)用中的實踐
Guava Cache是一個全內(nèi)存的本地緩存實現(xiàn),本文將講述如何將 Guava Cache緩存應(yīng)用到 Spring Boot應(yīng)用中。具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-01-01關(guān)于@ApiImplicitParams、ApiImplicitParam的使用說明
這篇文章主要介紹了關(guān)于@ApiImplicitParams、ApiImplicitParam的使用說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10讀取Java文件到byte數(shù)組的三種方法(總結(jié))
下面小編就為大家?guī)硪黄x取Java文件到byte數(shù)組的三種方法(總結(jié))。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-08-08Java Scala數(shù)據(jù)類型與變量常量及類和對象超詳細講解
本文內(nèi)容主要分為3節(jié),依次講解:Scala的數(shù)據(jù)類型有哪些? 變量常量如何使用? 類和對象如何理解? 受限于博主的大腦容量,大概是無法做到事無巨細的,不過其實也沒必要那么"細",抓住主要脈絡(luò),加上大量的練習(xí),融會貫通只不過是時間的問題2022-12-12Java 實戰(zhàn)項目錘煉之網(wǎng)上花店商城的實現(xiàn)流程
讀萬卷書不如行萬里路,只學(xué)書上的理論是遠遠不夠的,只有在實戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+jsp+servlet+mysql+ajax實現(xiàn)一個網(wǎng)上花店商城系統(tǒng),大家可以在過程中查缺補漏,提升水平2021-11-11SpringBoot?基于?MongoTemplate?的工具類過程詳解
MongoDB是一個高性能,開源,無模式的文檔型數(shù)據(jù)庫,是當(dāng)前NoSql數(shù)據(jù)庫中比較熱門的一種,這篇文章主要介紹了SpringBoot基于MongoTemplate的工具類,需要的朋友可以參考下2023-09-09