2011-03-11 70 views
13

我知道這是一個奇怪的問題要問在Java中, 但有一個辦法可以讓Java的動態內存分配的一些對齊約束對齊? 例如,是否可以動態分配與頁面大小對齊的對象?Java的內存分配對齊

我想這樣做的原因是因爲我要通過JNI接口從本地代碼訪問Java對象,本機代碼庫需要的對象對齊。

謝謝。

+0

你將不得不玩弄ByteBuffer.AllocateDirect()。 – 2011-03-11 02:11:13

回答

7

不,這是不可能的。請記住,Java堆中的對象可以在垃圾回收期間四處移動。你有一些選擇:

  1. 停止直接從JNI訪問Java對象。將您關心的內容複製到本機代碼已經對齊的JNI提供的緩衝區中。
  2. 使用ByteBuffer.allocateDirect()。然後找到正確對齊的緩衝區。一種方法是在啓動時分配緩衝區,並重復嘗試不同偏移量的對齊敏感操作,直到其工作。這是一個黑客!
+0

爲了進一步解釋,「JNI提供的緩衝區」應該是一些分配有「posix_memalign」(或類似的)並用JNI的「NewDirectByteBuffer」包裝的內存區域。將這個'ByteBuffer'返回給Java代碼。 Java可以像訪問數組一樣快速地訪問Buffers(ByteBuffers,FloatBuffers等)的元素。但請記住稍後以本機代碼手動釋放此內存。 – 2015-01-13 14:35:54

+2

重複分配和嘗試操作是一種*瘋狂*黑客攻擊,很容易導致崩潰或無限循環。請參閱Nitsan的更好方法的答案。 – 2015-01-13 14:38:33

1

有沒有美麗的選擇,所以這裏去醜的:

如果您使用的是Sun(現在是Oracle)JRE 5.0或6.0,可以使用以下命令:

ByteBuffer buffer = ByteBuffer.allocateDirect(pageSize); 
    Method getAddress = buffer.getClass().getMethod("address"); 
    long address = getAddress.invoke(buffer); 
    // and now send address to JNI 

要使用Java訪問數據,請使用緩衝區。要在JNI中訪問,請將地址轉換爲指針。兩者都會看到/更改相同的數據。

地址應該是頁對齊的,但可以肯定的是,你可以分配兩頁(肯定會有一個完整的頁對齊足夠的空間)。然後,您將頁面上的地址對齊,並將偏移量應用於ByteBuffer訪問。

另一種選擇緩衝區分配和本地調用,即在任何虛擬機的工作原理,是利用JNAs內存類:http://jna.java.net/javadoc/com/sun/jna/Memory.html。不要害怕com.sun包。它是開放源代碼和LGPL。

+0

用Nitsan的方法使用這個答案的一些元素是個好主意。也就是說,使用反射來獲取ByteBuffer的地址而不是UnsafeAccess。 – 2015-01-13 14:42:06

4

在Java中直接內存對齊的更廣泛的討論也涉及您的要求,請參閱本blog post。總結:

  1. 直到JDK 1.6所有的直接字節緩衝區頁面對齊,所以如果這是你的Java版本,你是排序。
  2. 從1.7開始,您可以依照後文中概述的方法獲取對齊的緩衝區。

我建議你去重複分配,直到你遇到正確的地址..這將是你的軟件非常糟糕。如果你打算重複地址,我建議你緩存它,如果你打算使用上面概述的反射方法。或者,使用不安全的帖子中概述的方法來獲取地址字段的值。

+0

這似乎是一個合理的方法,但我不會使用UnsafeAccess來獲取私有字段。使用簡單的反射(如fernacolo指出)。 – 2015-01-13 14:40:41

+0

@AleksandrDubinsky足夠公平,在我使用它的上下文中,我打算打開Unsafe case無論如何,但它可能不是第一個調用端口... – 2015-01-14 14:55:17

+2

原來,你也可以這樣做:'((DirectBuffer )ByteBuffer.allocateDirect(...))。地址()' – 2015-01-21 19:25:50