2011-03-11 69 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