Java – Write directly to memory
You have been told a lot that you can’t manage memory in Java. Well, it has changed since HotSpot release of the Java VM. There is a way to directly allocate and deallocate memory as well as write and read it… Of course we are talking about the JVM memory where the Java program runs. In this tutorial we are going to show you codes that deal with this topic.
1. Obtaining the sun.misc.Unsafe object
To work directly on a memory in Java you need to have a sun.misc.Unsafe
class instance. This class delivers a bunch of methods to work with the JVM memory directly. Obtaining this object is quite easy, here is a simple helper method that returns the Unsafe class instance.
private static Unsafe getUnsafe() throws Exception {
// Get the Unsafe object instance
Field field = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
return (sun.misc.Unsafe) field.get(null);
}
2. Writing and reading bytes directly from memory
Reading and writing bytes to the memory is very simple when you have the Unsafe object reference. All you need to do is allocating memory by calling the allocateMemory
method which returns the allocated memory address. To write a byte value just call the putAddress
method or the putByte
method of the sun.misc.Unsafe
class. To read a byte value you need to call the getAddress
or getByte
method. Look at the following example where we show how to write and read bytes.
public static void showBytes() {
try {
Unsafe unsafe = getUnsafe();
// Writing to a memory - MAX VALUE Byte
byte value = Byte.MAX_VALUE;
long bytes = 1;
// Allocate given memory size
long memoryAddress = unsafe.allocateMemory(bytes);
// Write value to the allocated memory
unsafe.putAddress(memoryAddress, value); // or putByte
// Output the value written and the memory address
System.out.println("[Byte] Writing " + value + " under the " + memoryAddress + " address.");
long readValue = unsafe.getAddress(memoryAddress); // or getByte
// Output the value from
System.out.println("[Byte] Reading " + readValue + " from the " + memoryAddress + " address.");
// C style! Release the Kraken... Memory!! :)
unsafe.freeMemory(memoryAddress);
} catch (Exception e) {
e.printStackTrace();
}
}
Using getAddress
and putAddress
methods is safe for bytes but if you want to read and write Long
values it is safer not to use these methods due to the fact that getAddress
method returns 32-bit long variable (if you will try to store read a value which is greater than 4294967295
, represented as 111111111111111111111111111111
you might not succeed).
3. Writing and reading Long values directly from memory
If you want to read and write Long values you can achieve it the same way as before but you need to use the putLong
and getLong
methods of the sun.misc.Unsafe
class. See the above snippet.
private static void showLong() {
try {
Unsafe unsafe = getUnsafe();
// Writing to a memory - MAX VALUE of Long
long value = Long.MAX_VALUE;
long bytes = Long.SIZE;
// Allocate given memory size
long memoryAddress = unsafe.allocateMemory(bytes);
// Write value to the allocated memory
unsafe.putLong(memoryAddress, value);
// Output the value written and the memory address
System.out.println("[Long] Writing " + value + " under the " + memoryAddress + " address.");
// Read the value from the memory
long readValue = unsafe.getLong(memoryAddress);
// Output the value from
System.out.println("[Long] Reading " + readValue + " from the " + memoryAddress + " address.");
// C style! Release the Kraken... Memory!! :)
unsafe.freeMemory(memoryAddress);
} catch (Exception e) {
e.printStackTrace();
}
}
4. Important! Deallocating memory.
It is very important to run the memory deallocation method – freeMemory
of the sun.misc.Unsafe
class. The below snippet shows what happens when you don’t free used memory. This code starts 100 threads where each of them loops 1000000 times allocating memory, writing and reading a random Long value.
public static void showDontFreeMemory() {
for (int t = 0; t < 100; t++) {
new Thread() {
public void run() {
System.out.println("Thread " + Thread.currentThread().getName() + " start!");
for (int i = 0; i < 1000000; i++) {
try {
Unsafe unsafe = getUnsafe();
// Writing random Long to a memory
long value = new Random().nextLong();
long bytes = Long.SIZE;
// Allocate given memory size
long memoryAddress = unsafe.allocateMemory(bytes);
// Write value to the allocated memory
unsafe.putLong(memoryAddress, value);
// Read the value from the memory
long readValue = unsafe.getLong(memoryAddress);
// Always free the memory !!
// ... FIXME: deallocate the memory used
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("Thread " + Thread.currentThread().getName() + " stop!");
};
}.start();
}
}
When you run the above method you’ll end up with the java.lang.OutOfMemoryError
thrown by the allocateMemory
method.
Exception in thread "Thread" java.lang.OutOfMemoryError
at sun.misc.Unsafe.allocateMemory(Native Method)
...
You will receive the above Exception when you will try to allocate too much memory as well. It will happen when you will try to run the below snippet.
public static void showAllocateTooMuch() {
try {
Unsafe unsafe = getUnsafe();
long bytes = Integer.MAX_VALUE; // It's way too much memory!!
// Allocate given memory size
long memoryAddress = unsafe.allocateMemory(bytes);
} catch (Exception e) {
e.printStackTrace();
}
}
These are only the basics of direct memory access in Java. You will find the complete running example below.
package com.itcuties;
import java.lang.reflect.Field;
import java.util.Random;
import sun.misc.Unsafe;
/**
* How to write directly to a memory.
*
* @author itcuties
*
*/
public class test {
public static void main(String[] args) {
// Uncomment to show how to read/write bytes
//showBytes();
// Uncomment to show how to read/write long values
showLong();
// Uncomment to show what happens when you don't free the memory
// showDontFreeMemory();
// Uncomment to show how does program run while the memory is being
// deallocated
// showFreeMemory();
// Uncomment to show what happens when you allocate to much memory
showAllocateTooMuch();
}
/**
* This method shows how to read/write bytes to the memory.
*/
public static void showBytes() {
try {
Unsafe unsafe = getUnsafe();
// Writing to a memory - MAX VALUE Byte
byte value = Byte.MAX_VALUE;
long bytes = 1;
// Allocate given memory size
long memoryAddress = unsafe.allocateMemory(bytes);
// Write value to the allocated memory
unsafe.putAddress(memoryAddress, value); // or putByte
// Output the value written and the memory address
System.out.println("[Byte] Writing " + value + " under the " + memoryAddress + " address.");
long readValue = unsafe.getAddress(memoryAddress); // or getByte
// Output the value from
System.out.println("[Byte] Reading " + readValue + " from the " + memoryAddress + " address.");
// C style! Release the Kraken... Memory!! :)
unsafe.freeMemory(memoryAddress);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* This method show how to read/write Long values to the memory.
*/
private static void showLong() {
try {
Unsafe unsafe = getUnsafe();
// Writing to a memory - MAX VALUE of Long
long value = Long.MAX_VALUE;
long bytes = Long.SIZE;
// Allocate given memory size
long memoryAddress = unsafe.allocateMemory(bytes);
// Write value to the allocated memory
unsafe.putLong(memoryAddress, value);
// Output the value written and the memory address
System.out.println("[Long] Writing " + value + " under the " + memoryAddress + " address.");
// Read the value from the memory
long readValue = unsafe.getLong(memoryAddress);
// Output the value from
System.out.println("[Long] Reading " + readValue + " from the " + memoryAddress + " address.");
// C style! Release the Kraken... Memory!! :)
unsafe.freeMemory(memoryAddress);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* This method show what happens when you don't deallocate memory. We start
* 100 threads where each one is allocating memory for a Long value and
* writes it 1000000 times without deallocating memory.
*/
public static void showDontFreeMemory() {
for (int t = 0; t < 100; t++) {
new Thread() {
public void run() {
System.out.println("Thread " + Thread.currentThread().getName() + " start!");
for (int i = 0; i < 1000000; i++) {
try {
Unsafe unsafe = getUnsafe();
// Writing random Long to a memory
long value = new Random().nextLong();
long bytes = Long.SIZE;
// Allocate given memory size
long memoryAddress = unsafe.allocateMemory(bytes);
// Write value to the allocated memory
unsafe.putLong(memoryAddress, value);
// Read the value from the memory
long readValue = unsafe.getLong(memoryAddress);
// Always free the memory !!
// ... FIXME: deallocate the memory used
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("Thread " + Thread.currentThread().getName() + " stop!");
};
}.start();
}
}
/**
* This method code shows how you should properly allocate and deallocate
* memory. We start 100 threads where each one is allocating memory for a
* Long value and writes it 1000000 times with deallocating memory.
*/
public static void showFreeMemory() {
for (int t = 0; t < 100; t++) {
new Thread() {
public void run() {
System.out.println("Thread " + Thread.currentThread().getName() + " start!");
for (int i = 0; i < 1000000; i++) {
try {
Unsafe unsafe = getUnsafe();
// Writing random Long to a memory
long value = new Random().nextLong();
long bytes = Long.SIZE;
// Allocate given memory size
long memoryAddress = unsafe.allocateMemory(bytes);
// Write value to the allocated memory
unsafe.putLong(memoryAddress, value);
// Read the value from the memory
long readValue = unsafe.getLong(memoryAddress);
// Always free the memory !!
unsafe.freeMemory(memoryAddress);
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("Thread " + Thread.currentThread().getName() + " stop!");
};
}.start();
}
}
/**
* This method shows what happens when you try to allocate to much memory at
* a time.
*/
public static void showAllocateTooMuch() {
try {
Unsafe unsafe = getUnsafe();
long bytes = Integer.MAX_VALUE; // It's way too much memory!!
// Allocate given memory size
long memoryAddress = unsafe.allocateMemory(bytes);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Get the Unsafe object instance.
*
* @return
* @throws Exception
*/
private static Unsafe getUnsafe() throws Exception {
// Get the Unsafe object instance
Field field = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
return (sun.misc.Unsafe) field.get(null);
}
}
there is any example with allocate memory but for object?
For more information, please refer to this url – http://mishadoff.github.io/blog/java-magic-part-4-sun-dot-misc-dot-unsafe/
Thanks for this tutorial. Is if possible to read a data block from memory? I mean for example 32*8 byte array.
Section 2 says that either putAddress or putByte can be used to write a byte to memory. The code example then uses putAddress after allocating one byte. According to the following site, putAddress is used for storing a native pointer to a memory address. Since a native pointer is either 4 or 8 bytes, I believe that putAddress isn’t interchangeable with putByte, and would require more than one byte allocated in the call to allocateMemory.
http://www.docjar.com/html/api/sun/misc/Unsafe.java.html