Java memory allocation alignment
Asked Answered
S

4

17

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 example, 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.

Slate answered 11/3, 2011 at 1:46 Comment(1)
You'll have to play around with ByteBuffer.AllocateDirect().Godfearing
H
8

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:

  1. Stop accessing Java objects directly from JNI. Copy what you care about into a JNI-provided buffer that the native code has already aligned.
  2. 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!
Hunchback answered 11/3, 2011 at 2:1 Comment(2)
To explain a bit further, the "JNI-provided buffer" should be some memory region allocated with posix_memalign (or similar) and wrapped with JNI's NewDirectByteBuffer. Return this ByteBuffer to the Java code. Java can access elements of Buffers (ByteBuffers, FloatBuffers, etc.) as quickly as it accesses arrays. But remember to later free this memory manually in native code.Mathia
Repeatedly allocating and trying the operation is a crazy hack, which can easily result in a crash or an endless loop. See Nitsan's answer for a better approach.Mathia
R
5

See this blog post for wider discussion of direct memory alignment in java which also covers your requirement. To summarize:

  1. 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.
  2. 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.

Reentry answered 8/1, 2013 at 15:8 Comment(3)
This seems like a reasonable approach, except I wouldn't use UnsafeAccess just to grab a private field. Use plain reflection (like fernacolo pointed out).Mathia
@AleksandrDubinsky fair enough, at the context I was using it I was going to crack open the Unsafe case anyhow, but it's probably not the first port of call...Reentry
Turns out, you can also just do: ((DirectBuffer)ByteBuffer.allocateDirect(...)).address()Mathia
I
3

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:

    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);
        }
    }
Inanimate answered 6/9, 2022 at 16:59 Comment(0)
P
2

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.

Privity answered 11/3, 2011 at 2:18 Comment(1)
It's a good idea to use some elements of this answer with the Nitsan's approach. Namely, use reflection to get the ByteBuffer's address rather than UnsafeAccess.Mathia

© 2022 - 2024 — McMap. All rights reserved.