久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

Java直接內存訪問的技巧有哪些

219次閱讀
沒有評論

共計 3904 個字符,預計需要花費 10 分鐘才能閱讀完成。

本篇文章給大家分享的是有關 Java 直接內存訪問的技巧有哪些,丸趣 TV 小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著丸趣 TV 小編一起來看看吧。

Java 直接內存訪問的技巧

Java 被設計成一個安全,可管理的環境,然而 Java HotSpot 有一個后門,提供了對低級別的,對直接內存和線程的操作。這個后門是—-sun.misc.Unsafe。這個類在 JDK 中有廣泛的應用,例如,java.nio 和 java.util.concurrent。很難想象在日常開發中使用這些危險的,不可移植和未經校驗的 API。然而,Unsafe 提供一種簡單的方法來觀察 HotSpot JVM 內部的一些技巧。

獲取 Unsafe

sun.misc.Unsafe 這個類的訪問是受限的,它的構造方法是私有的,相應的工廠方法要求必須被 Bootloader 載入才能使用,也就是說,只有 JDK 內部分才能使用這個工廠方法來構造 Unsafe 對象。

1

2

3

4

5

6

7

8

9

10

11

12

13

public final class Unsafe {

 …

 private Unsafe() {}

 private static final Unsafe theUnsafe = new Unsafe();

 …

 public static Unsafe getUnsafe() {

 Class cc = sun.reflect.Reflection.getCallerClass(2);

 if (cc.getClassLoader() != null)

 throw new SecurityException(Unsafe);

 return theUnsafe;

 }

 …

}

幸運地是,有一個 theUnsafe 屬性可以被利用來檢索 Unsafe 實例,我們可以見到的寫一個反射方法,來獲取 Unsafe 實例:

1

2

3

4

5

6

7

public static Unsafe getUnsafe() {

try {

Field f = Unsafe.class.getDeclaredField(theUnsafe);

f.setAccessible(true);

return (Unsafe)f.get(null);

} catch (Exception e) {/* … */}

}

下面將學習一些 Unsafe 的方法。

1.long getAddress(long address)  和 void putAddress(long address, long x) 
對直接內存進行讀寫。

2.int getInt(Object o, long offset) , void putInt(Object o, long offset, int x)

另一個類似的方法對直接內存進行讀寫,將 C 語言的結構體和 Java 對象進行轉換。

3.long allocateMemory(long bytes)

這個可以看做是 C 語言的 malloc() 函數的一種包裝。

sizeof() 函數

Java 對象的結構如下圖所示:

第一個技巧,是模擬 C 語言的 sizefo() 函數,這個函數返回對象的字節大小。我們可以用如下的代碼實現 sizeof() 函數:

1

2

3

4

5

6

7

8

9

public static long sizeOf(Object object) {

 Unsafe unsafe = getUnsafe();

 return unsafe.getAddress(normalize( unsafe.getInt(object, 4L) ) + 12L );

}

 

public static long normalize(int value) {

 if(value = 0) return value;

 return (~0L 32) value;

}

我們需要使用 normalize() 函數,因為如果內存地址如果在 2^31 和 2^32 之間,將會自動的覆蓋鄰近的整型,也就是說用補碼的方式進行存儲。讓我們在 32 位 JVM(JDK6 或者 7)中進行測試:

1

2

3

4

5

// 執行 sizeOf(new MyStructure()) 得到如下的結果:

 

class MyStructure {} // 8: 4 ( 起始標記) + 4 (指向類的指針)

class MyStructure {int x;} // 16: 4 (起始標記) + 4 (指向類的指針) + 4 (int) + 4 填充字節用來對齊 64 位塊

class MyStructure {int x; int y;} // 16: 4 (起始標記) + 4 (指向類的指針) + 2*4

直接內存管理

Unsafe 允許通過 allcateMemory 和 freeMemory 方法對內存進行顯示的分配和回收,直接分配的內存不在 GC 的控制內,并且不受限于 JVM 堆的大小。通常,通過 NIO 的脫離堆約束的緩沖,這些方法是安全有效的,但是有趣的是這讓標準的 Java 引用映射非堆內存變成了可能:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

MyStructure structure = new MyStructure(); // create a test object

structure.x = 777;

 

long size = sizeOf(structure);

long offheapPointer = getUnsafe().allocateMemory(size);

getUnsafe().copyMemory(

 structure,  // source object

 0,  // source offset is zero – copy an entire object

 null,  // destination is specified by absolute address, so destination object is null

 offheapPointer, // destination address

 size

); // test object was copied to off-heap

 

Pointer p = new Pointer(); // Pointer is just a handler that stores address of some object

long pointerOffset = getUnsafe().objectFieldOffset(Pointer.class.getDeclaredField( pointer));

getUnsafe().putLong(p, pointerOffset, offheapPointer); // set pointer to off-heap copy of the test object

 

structure.x = 222; // rewrite x value in the original object

System.out.println(  ((MyStructure)p.pointer).x  ); // prints 777

 

….

 

class Pointer {

 Object pointer;

}

所以,事實上是可以對真實對象進行內存分配和回收的,不單單只是 NIO 中的字節緩沖。當然,有一個比較大的問題是,GC 將會在這樣的內存欺騙之后發生。

繼承自 Final 類和 void*

想象一下有一個以 String 為參數的方法,但它需要經行外部的重載。具體代碼如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

Carrier carrier = new Carrier();

carrier.secret = 777;

 

String message = (String)(Object)carrier; // ClassCastException

handler(message);

 

 

void handler(String message) {

 System.out.println(((Carrier)(Object)message).secret );

}

 

 

class Carrier {

 int secret;

}

為了讓這段代碼能工作,首先需要更改 Carrier 類去偽裝成 String 的子類。superclasses 列表被存儲在 Carrier 類結構體 28 的位置,如上文圖中所示。原則上,添加如下的代碼可以讓 Carrer 轉化成 String:

1

2

3

long carrierClassAddress = normalize(unsafe.getInt(carrier, 4L) );

long stringClassAddress = normalize(unsafe.getInt( , 4L) );

unsafe.putAddress(carrierClassAddress + 32, stringClassAddress); // insert pointer to String class to the list of Carrier s superclasses

這樣,類型轉化可以正常工作。然而,這樣的轉換方式是不切當并且違反虛擬機規范的。更詳細的方法將包含如下的步驟:

1. 在 Carrier 類中 32 的位置實際上包含了一個指向 Carrier 類自己的指針,所以這個指針將被轉移到 36 的位置上,不僅僅是被指針重寫到 String 類。

2. 當 Carrier 繼承自 String 的時候,String 類的 final 標記將被移掉。

sun.misc.Unsafe 提供了幾乎是不受限制的監控和修改虛擬機運行時數據結構的能力。盡管這些能力幾乎是和 Java 開發本身不相干的,但是對于想要學習 HotSpot 虛擬機但是沒有 C ++ 代碼調試,或者需要去創建特別的分析工具的人來說,Unsafe 是一個偉大的工具。

以上就是 Java 直接內存訪問的技巧有哪些,丸趣 TV 小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注丸趣 TV 行業資訊頻道。

正文完
 
丸趣
版權聲明:本站原創文章,由 丸趣 2023-08-25發表,共計3904字。
轉載說明:除特殊說明外本站除技術相關以外文章皆由網絡搜集發布,轉載請注明出處。
評論(沒有評論)
主站蜘蛛池模板: 全椒县| 元氏县| 马龙县| 喀什市| 泸水县| 伊通| 疏勒县| 闻喜县| 女性| 石阡县| 河南省| 南宫市| 长顺县| 新干县| 长阳| 衡东县| 隆安县| 龙岩市| 湄潭县| 宁安市| 高要市| 新竹县| 丰镇市| 望城县| 沐川县| 自贡市| 延川县| 玛纳斯县| 常熟市| 大庆市| 麻城市| 渑池县| 榕江县| 桐梓县| 渭南市| 新邵县| 阿荣旗| 连平县| 桓台县| 玉屏| 台东县|