Java memory allocation alignment
I know this is a weird question to ask in Java, but is there a way to let Java dynamic memory allocation be aligned with some alignment constraints? For exampl开发者_运维百科e, is it possible to dynamically allocate objects that are aligned with the page size?
The reason I want to do this is because I'm going to access the Java object from native code through JNI interface, and the native code library requires the object to be aligned.
Thanks.
No it is not possible. Keep in mind that objects on the heap in Java can move around during garbage collection. You have some options:
- Stop accessing Java objects directly from JNI. Copy what you care about into a JNI-provided buffer that the native code has already aligned.
- Use ByteBuffer.allocateDirect(). Then find a region of your buffer that is correctly aligned. One way to do that is to allocate the buffer at startup and repeatedly attempt the alignment sensitive operation at different offsets until it works. This is a hack!
See this blog post for wider discussion of direct memory alignment in java which also covers your requirement. To summarize:
- Up to JDK 1.6 all direct byte buffers are page aligned, so if that's the version of java you are on you're sorted.
- From 1.7 you are on your own, follow the method outlined in the post to get an aligned buffer.
I would recommend you do not go for repeated allocations until you hit the right address.. That would be very bad for your software. If you plan to grab the address repeatedly I'd recommend you cache it if you plan to use the reflection method outlined above. Alternatively use the method outlined in the post using Unsafe to grab the value of the address field.
There are no beautifull options, so here go the ugly ones:
If you are using Sun (now Oracle) JRE 5.0 or 6.0, you can use the following:
ByteBuffer buffer = ByteBuffer.allocateDirect(pageSize);
Method getAddress = buffer.getClass().getMethod("address");
long address = getAddress.invoke(buffer);
// and now send address to JNI
To access data in Java, use buffer. To access in JNI, cast address to a pointer. Both will see/change the same data.
The address is supposed to be page-aligned, but to be sure of that, you can allocate two pages (surely there will be enough space for a full aligned page). Then you align the address on the page and apply an offset to ByteBuffer access.
Another option for buffer allocation and native calls, that works on any VM, is using JNAs Memory class: http://jna.java.net/javadoc/com/sun/jna/Memory.html. Don't be scared with com.sun package. It's open-source and LGPL.
2022 Update:
Use .alignedSlice()
. The below code is a working example of dynamically allocating aligned memory to the size of the disk's page size:
- https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/nio/ByteBuffer.html#alignedSlice(int)
private static final int SIZE = 4096;
private static void testWrite(Path p) throws Exception {
try (FileChannel fc = FileChannel.open(p, StandardOpenOption.WRITE,
ExtendedOpenOption.DIRECT)) {
FileStore fs = Files.getFileStore(p);
int alignment = (int)fs.getBlockSize();
ByteBuffer src = ByteBuffer.allocateDirect(SIZE + alignment - 1)
.alignedSlice(alignment);
for (int j = 0; j < SIZE; j++) {
src.put((byte)0);
}
src.flip();
fc.write(src);
}
}
private static void testRead(Path p) throws Exception {
try (FileChannel fc = FileChannel.open(p, ExtendedOpenOption.DIRECT)) {
FileStore fs = Files.getFileStore(p);
int alignment = (int)fs.getBlockSize();
ByteBuffer dest = ByteBuffer.allocateDirect(SIZE + alignment - 1)
.alignedSlice(alignment);
fc.read(dest);
}
}
精彩评论