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);
  }

}

Download Source Code

About the Author

author image
ITCuties
Programming tutorials hosted by cute girls. Tutorials, code samples, real life examples , Android development, WordPress, SQL, Java and many more...

Comments

avatar
4 Comment threads
0 Thread replies
1 Followers
 
Most reacted comment
Hottest comment thread
4 Comment authors
dmitryblueDanielmkyong Recent comment authors
newest oldest most voted
dmitry
Guest
dmitry

there is any example with allocate memory but for object?

mkyong
Guest
mkyong

For more information, please refer to this url – http://mishadoff.github.io/blog/java-magic-part-4-sun-dot-misc-dot-unsafe/

blue
Guest
blue

Thanks for this tutorial. Is if possible to read a data block from memory? I mean for example 32*8 byte array.

Daniel
Guest
Daniel

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