Java –メモリに直接書き込む
Javaでメモリを管理することはできないとよく言われています。 それは、Java VMのHotSpotリリース以降に変更されました。 メモリを直接読み書きするだけでなく、メモリの割り当てと割り当て解除を直接行う方法があります...もちろん、Javaプログラムが実行されるJVMメモリについて説明しています。 このチュートリアルでは、このトピックを扱うコードを紹介します。
1. sun.misc.Unsafeオブジェクトの取得
Javaのメモリを直接操作するには、sun.misc.Unsafe
クラスインスタンスが必要です。 このクラスは、JVMメモリを直接操作するための多数のメソッドを提供します。 このオブジェクトの取得は非常に簡単です。Unsafeクラスインスタンスを返す単純なヘルパーメソッドを次に示します。
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. メモリから直接バイトを読み書きする
安全でないオブジェクト参照がある場合、メモリへのバイトの読み取りと書き込みは非常に簡単です。 割り当てられたメモリアドレスを返すallocateMemory
メソッドを呼び出して、メモリを割り当てるだけです。 バイト値を書き込むには、putAddress
メソッドまたはsun.misc.Unsafe
クラスのputByte
メソッドを呼び出すだけです。 バイト値を読み取るには、getAddress
またはgetByte
メソッドを呼び出す必要があります。 バイトの書き込みと読み取りの方法を示す次の例を見てください。
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(); } }
getAddress
およびputAddress
メソッドを使用することはバイトに対して安全ですが、Long
値を読み書きする場合は、getAddress
メソッドであるため、これらのメソッドを使用しない方が安全です。 32-bitの長い変数を返します(111111111111111111111111111111
として表される4294967295
より大きい値を格納しようとすると、成功しない可能性があります)。
3. ロング値をメモリから直接読み書きする
Long値の読み取りと書き込みを行う場合は、以前と同じ方法で実現できますが、sun.misc.Unsafe
クラスのputLong
メソッドとgetLong
メソッドを使用する必要があります。 上記のスニペットを参照してください。
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. 重要! メモリの割り当て解除。
sun.misc.Unsafe
クラスのfreeMemory
というメモリ割り当て解除メソッドを実行することは非常に重要です。 以下のスニペットは、使用済みメモリを解放しないとどうなるかを示しています。 このコードは100個のスレッドを開始し、それぞれが1000000回ループしてメモリを割り当て、ランダムなLong値を読み書きします。
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(); } }
上記のメソッドを実行すると、allocateMemory
メソッドによってスローされるjava.lang.OutOfMemoryError
になります。
Exception in thread "Thread" java.lang.OutOfMemoryError at sun.misc.Unsafe.allocateMemory(Native Method) ...
あまりにも多くのメモリを割り当てようとすると、上記の例外が発生します。 以下のスニペットを実行しようとすると発生します。
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(); } }
これらは、Javaのダイレクトメモリアクセスの基本にすぎません。 完全な実行例を以下に示します。
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); } }
ソースコードをダウンロード
ダウンロード-java-write-directly-to-memory.zip(4 kb)