Java - メモリに直接書き込む

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)