Sometimes you have to hack. You just have to. Don’t listen to XKCD. You don’t always regret hacking. On our blog, we’ve shown a couple of hacks before:
- Throw checked exceptions like runtime exceptions in Java
- A dirt-ugly hack to modify private final fields in Java
But we’ve just been scratching the surface. Our friends at ZeroTurnaround / RebelLabs have recently published an awesome article about how to use “the Unsafe”. The sun.misc.Unsafe
class to directly access memory in Java. While the first page introduces us to the Unsafe object itself and how to access it through reflection …
public static Unsafe getUnsafe() {
try {
Field f = Unsafe.class
.getDeclaredField("theUnsafe");
f.setAccessible(true);
return (Unsafe) f.get(null);
} catch (Exception e) {
/* ... */
}
}
… subsequent sections nicely explain how to map “unsafe” memory access methods to addressing a Class in memory, of objects in memory …
// If you're daring, go manipulate the heap directly!
Object helperArray[] = new Object[1];
helperArray[0] = targetObject;
long baseOffset =
unsafe.arrayBaseOffset(Object[].class);
long addressOfObject =
unsafe.getLong(helperArray, baseOffset);
However, don’t think it’s so easy. In order to manipulate the Java heap directly, you will need to understand a lot about the various fields and flags in class headers, and you’ll always need to remember to distinguish between 32-bit and 64-bit JVMs.
When All Else Fails: Using “the Unsafe” … will also. This is not without reason why the name is “unsafe”. The major problem is that the addresses acquired via unsafe will not change when a parallel executing GC just moves the objects. And then you end up with a code that will fail the same old, dreadful and miserable way. Core dump. No more information.
Was it that we changed C++ for Java?
It depends on what you’re doing. Consider the following code snippet from
com.google.common.primitives.UnsignedBytes$LexicographicalComparatorHolder$UnsafeComparator$1
, for instance:I trust their benchmark really showed significant improvements in the above code. And I suspect that a single call to
getLong()
is atomic, so the GC won’t interfere here.It’s not always easy to get it right, and there are terrible ways to get it wrong. But it’s not necessarily a bad idea to use the “unsafe”
You are right. Nevertheless the use of unsafe should very much be limited. Not an everyday task and I would not pass code directly using it in an enterprise application.
Any blog referring to use of unsafe should press very much how dangerous this can be.
Doesn’t the name “unsafe” and the fact that we need to access it through reflection keep us from using it, the reasonable and responsible engineers that we are? :-)
Juniors also read your blog. I know, because I recommended it for them. Well, I can not “know”. I hope they do.
Alright. I should write an article about how to write
goto
semantics in Java ;-)I do trust Google’s benchmark, but unless you do this in a long loop, the performance benefits will be minimal.
If you do it in a long loop, and profiling has shown that it makes your app unacceptably slow, chances are it isn’t the only expensive operation, and it makes sense to use JNI to call a library you specifically write in portable C.
Unsafe has one more problem: it’s not thread-safe. In your bytes example above, you have no way to use Unsafe ina thread-safe way. Therefore, using Unsafe in one place nails down several design constraints which will give you headaches later.
I remember what a guy named A. J. Riel said in a book which IMO every programmer should read: whenever you feel like you need multiple inheritance, assume you don’t and try to prove otherwise. I’d apply similar thinking to using Unsafe.