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.メモリから直接バイトを書き込んで読み込む
Unsafeオブジェクト参照がある場合、メモリへのバイトの読み書きは非常に簡単です。あなたがする必要があるのは、割り当てられたメモリアドレスを返す
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ビット
long変数を返すので、これらのメソッドを使用しない方が安全です(読み込みに
4294967295`より大きい値を格納しようとすると、
111111111111111111111111111111`と表されますが、成功しない可能性があります)。
3. Long値をメモリから直接読み書きする
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();
}
}
重要!メモリの割り当てを解除します.
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);
}
}