userx
userx

Reputation: 3773

Buffer vs Unsafe - Outside JVM

I have a requirement to use a space in the available RAM which the GC has no control on. I read a few articles on the same which gave me the introduction on two approaches. They are specified in the following code.

package com.directmemory;

import java.lang.reflect.Field; import java.nio.ByteBuffer;

import sun.misc.Unsafe;

public class DirectMemoryTest {

public static void main(String[] args) {

    //Approach 1
    ByteBuffer directByteBuffer = ByteBuffer.allocateDirect(8);
    directByteBuffer.putDouble(1.0);
    directByteBuffer.flip();
    System.out.println(directByteBuffer.getDouble());

    //Approach 2
    Unsafe unsafe = getUnsafe();
    long pointer = unsafe.allocateMemory(8);
    unsafe.putDouble(pointer, 2.0);
    unsafe.putDouble(pointer+8, 3.0);

    System.out.println(unsafe.getDouble(pointer));
    System.out.println(unsafe.getDouble(pointer+8));
    System.out.println(unsafe.getDouble(pointer+16));
}

public static Unsafe getUnsafe() {
    try {
        Field f = Unsafe.class.getDeclaredField("theUnsafe");
        f.setAccessible(true);
        return (Unsafe) f.get(null);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

}

I have a couple of questions

1) Why should I ever pay attention to Approach 1 mentioned in the code because as per my understanding ByteBuffer.allocateDirect() cannot return me a buffer with a storage capacity of greater than 2GB ? So if my requirement is to store say 3 GB of data, I have to create a new buffer and store the data there which would mean that apart from storing data I have additional responsibility of identifying the respective buffer (out of the list of 'n' buffers) which maintains a pointer to direct memory.

2) Isn't approach 2 a little faster than approach 1 because I don't have to first find the buffer and then the data, I just need an indexing mechanism of for an object's field and use getDouble/getInt methods and pass the absolute address ?

3) Is the allocation of direct memory (is it right to say off heap memory ?) related to a PID ? If on a machine, I have 2 java processes, allocateMemory calls in PID 1 and PID 2 give me never intersecting memory blocks to use ?

4) Why is the last sysout statement not resulting in 0.0 ? The idea is that every double uses 8 bytes so I store 1.0 at address returned by allocateMemory say address = 1, the 2.0 at address 1+8 which is 9 and then stop. So shouldn't the default value be 0.0 ?

Upvotes: 2

Views: 2778

Answers (1)

Thomas Kläger
Thomas Kläger

Reputation: 21510

One point to consider is that sun.misc.Unsafe is not a supported API. It will be replaced by something else (http://openjdk.java.net/jeps/260)

1) If your code must run unchanged with Java 8 to Java 10 (and later), approach 1 with ByteBuffers is the way to go.

If you're ready to replace the use of sun.misc.Unsafe with whatever replace is in Java 9 / Java 10 you may well go with sun.misc.Unsafe.

2) For very large data structures with more than 2 GBytes approach 2 might be faster due to the necessary additional indirection in approach 1. However without a solid (micro) benchmark I would not bet anything on it.

3) The allocated memory is always bound to the currently running JVM. So with two JVMs running on the same machine you will not get intersecting memory.

4) You are allocating 8 bytes of uninitialized memory. The only amount of memory you may legally access now is 8 bytes. For the memory beyond your allocated size no guarantees are made.

4a) You are writing 8 bytes beyond your allocated memory (unsafe.putDouble(pointer+8, 3.0);), which already leads to memory corruption and can lead to a JVM crash on the next memory allocation.

4b) You are reading 16 bytes beyond your allocated memory, which (depending on your processor architecture and operating system and previous memory use) can lead to an immediate JVM crash.

Upvotes: 7

Related Questions