Java - Ecrire directement dans la mémoire

Java - Ecrivez directement dans la mémoire

On vous a souvent dit que vous ne pouvez pas gérer la mémoire en Java. Eh bien, cela a changé depuis la version HotSpot de la machine virtuelle Java. Il existe un moyen d'allouer et de désallouer directement la mémoire ainsi que de l'écrire et de la lire… Bien sûr, nous parlons de la mémoire JVM sur laquelle s'exécute le programme Java. Dans ce tutoriel, nous allons vous montrer les codes qui traitent de ce sujet.

1. Obtention de l'objet sun.misc.Unsafe

Pour travailler directement sur une mémoire en Java, vous devez avoir une instance de classesun.misc.Unsafe. Cette classe fournit un tas de méthodes pour travailler directement avec la mémoire JVM. L'obtention de cet objet est assez facile, voici une méthode d'assistance simple qui retourne l'instance de classe 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. Écriture et lecture d'octets directement depuis la mémoire

La lecture et l'écriture d'octets dans la mémoire est très simple lorsque vous disposez de la référence d'objet non sécurisé. Tout ce que vous avez à faire est d'allouer de la mémoire en appelant la méthodeallocateMemory qui renvoie l'adresse mémoire allouée. Pour écrire une valeur d'octet, appelez simplement la méthodeputAddress ou la méthodeputByte de la classesun.misc.Unsafe. Pour lire une valeur d'octet, vous devez appeler la méthodegetAddress ougetByte. Regardez l'exemple suivant où nous montrons comment écrire et lire des octets.

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

L'utilisation des méthodesgetAddress etputAddress est sûre pour les octets, mais si vous voulez lire et écrire les valeursLong, il est plus sûr de ne pas utiliser ces méthodes car la méthodegetAddress renvoie la variable longue32-bit (si vous essayez de stocker en lecture une valeur supérieure à4294967295, représentée par111111111111111111111111111111, vous risquez de ne pas réussir).

3. Écriture et lecture de valeurs longues directement depuis la mémoire

Si vous voulez lire et écrire des valeurs Long, vous pouvez y parvenir de la même manière qu'auparavant, mais vous devez utiliser les méthodesputLong etgetLong de la classesun.misc.Unsafe. Voir l'extrait ci-dessus.

  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! Désallocation de la mémoire.

Il est très important d'exécuter la méthode de désallocation de la mémoire -freeMemory de la classesun.misc.Unsafe. L'extrait ci-dessous montre ce qui se passe lorsque vous ne libérez pas la mémoire utilisée. Ce code démarre 100 threads où chacun d'eux boucle 1000000 fois en allouant de la mémoire, en écrivant et en lisant une valeur Long aléatoire.

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

Lorsque vous exécutez la méthode ci-dessus, vous vous retrouvez avec lesjava.lang.OutOfMemoryError lancés par la méthodeallocateMemory.

Exception in thread "Thread" java.lang.OutOfMemoryError
        at sun.misc.Unsafe.allocateMemory(Native Method)
        ...

Vous recevrez l'exception ci-dessus lorsque vous essaierez d'allouer trop de mémoire également. Cela se produira lorsque vous essaierez d'exécuter l'extrait ci-dessous.

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

Ce ne sont que les bases de l'accès direct à la mémoire en Java. Vous trouverez l'exemple de fonctionnement complet ci-dessous.

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

}

Télécharger le code source

Télécharger -java-write-directly-to-memory.zip (4 ko)