9

I'm struggling to understand the documentation of sun.misc.Unsafe -- I guess as it's not intended for general use, nobody's really bothered with making it readable -- but I actually really need a way to find the address of an element in an array (so that I can pass a pointer to it to native code). Has anyone got any working code that does this? Is it reliable?

13
  • stackoverflow.com/questions/5574241/… Commented Oct 28, 2011 at 20:52
  • Why Developers Should Not Write Programs That Call 'sun' Packages Commented Oct 28, 2011 at 20:58
  • imo, unsafe doc is pretty good, it's not intended for general public anyways. if you need some guidelines, start reading java.util.concurrent and java.util.concurrent.atomic... unsafe is pretty close to C (or assembler, if you prefer). If you have no experience if any, unsafe is not for you. How to get disassemble of your java code: wikis.sun.com/display/HotSpotInternals/PrintAssembly Commented Oct 28, 2011 at 21:27
  • 1
    @BalusC, unsafe is good and proper if you know what you do (i.e. not asking question on SO about it :) ) and it's probably more portable than JNI. Commented Oct 28, 2011 at 21:27
  • 1
    @bestsss - so do you know a reliable way to stop the GC from moving an object while you are using the pointer returned by Unsafe? Commented Oct 29, 2011 at 4:04

3 Answers 3

9

Instead of using an array you can use a ByteBuffer.allocateDirect() direct buffer. This has the address in a field and this address doesn't change for the life of the ByteBuffer. A direct ByteBuffer uses minimal heap space. You can get the address using reflection.


You can use Unsafe to get an address, the problem is that the GC can move it at any time. Objects are not fixed in memory.

In JNI you can use special methods to copy data to/from Java objects to avoid this issue (and others) I suggest you use these if you want to exchange data between Objects with C code.

Sign up to request clarification or add additional context in comments.

3 Comments

I'd rather not use a direct byte buffer (which would make solving the problem rather easy) as I am stuck trying to implement an existing API which is defined in terms of byte arrays, and am trying to avoid the penalty of copying to and from an extra set of buffers. This would suffice as a last resort, but there must be some better way. Working with JNI would make this easier, but unfortunately I am working with JNA, which doesn't seem to have the interfaces necessary to do anything other than work with entire arrays.
@Jules, JNI will cause a copy on its own, unless you wish to go w/ GetPrimitiveArrayCritical which might hurt the GC. Bite the bullet and copy into direct (buffer) memory. That's the only feasible solution. For instance the impl. FileOutputStream (SocketOutputStream extends it) uses copy of the elements on the stack. Penalty to copy is not so high, since the highest cost comes w/ the load cost of the data (and cache misses, even) which you'd have to pay either way. The copying will also cause to prefetch the cachelines, so it's might be even better depeding how the native code works.
@Jules, on a side note: even javax.net.ssl.SSLEngine allows use of buffers, all the algorithms are byte[] and they end up copying direct buffers into temporary arrays, this is the reverse story and the moral of it: do not use direct buffers w/ SSLEngine.
9

Here is a working sample. Please be careful however as you may easily crash your JVM with unappropriate usage of Unsafe class.

import java.lang.reflect.Field;

import sun.misc.Unsafe;

public class UnsafeTest {

    public static void main(String... args) {
        Unsafe unsafe = null;

        try {
            Field field = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (sun.misc.Unsafe) field.get(null);
        } catch (Exception e) {
            throw new AssertionError(e);
        }

        int ten = 10;
        byte size = 1;
        long mem = unsafe.allocateMemory(size);
        unsafe.putAddress(mem, ten);
        long readValue = unsafe.getAddress(mem);
        System.out.println("Val: " + readValue);

    }
}

3 Comments

That code must be perfectly safe, as it is exactly how DirectByteBuffer works. Unfortunately, it doesn't do what I want, which is to access the data in an existing array.
@StephenC, the code is perfectly fine, it's basically char* mem = malloc(size);... it's never touched by the GC and causes native C leak unless realesed.
The code is fine, but you also need to consider freeing the allocated memory. See DirectByteBuffer for one way of doing it. Also consider that allocated memory is not zeroed, so you can not assume your direct array is zero initialized.
0

Why? There are plenty of facilities in JNI for dealing with the contents of Java arrays. You don't need to use undocumented internal Sun classes that mightn't be there next week.

2 Comments

I'm using JNA, rather than JNI, and the function I'm interfacing with needs to have a pointer passed to the middle of an array, whereas JNA only appears to be able to produce a pointer to the start of an array.
@Jules, do not mix JNA and normal arrays, use direct buffers.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.