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)