Parte 1 de JVM_11_Memoria directa_Silicon Valley

Descripción general de la memoria directa

  • No es parte del área de datos de tiempo de ejecución de la máquina virtual, ni es un área de memoria definida en la especificación de máquina virtual de Java.
  • La memoria directa es el rango de memoria fuera del montón de Java que se aplica directamente al sistema.
  • Originado en NIO, opera la memoria nativa a través de DirectByteBuffer almacenado en el montón
  • Por lo general, la velocidad de acceso a la memoria directa se debe al montón de Java, es decir, un alto rendimiento de lectura y escritura.
    • Debido a consideraciones de rendimiento, se puede considerar la memoria directa para ocasiones con lecturas y escrituras frecuentes.
    • La biblioteca NIO de Java permite que los programas de Java usen memoria directa para búferes de datos

Ejemplo de código de huella de memoria directa

/**
 * 查看直接内存的占用与释放
 */
public class BufferTest {
    
    

  private static final int BUFFER = 1024 * 1024 * 1024; // 1GB

  public static void main(String[] args) {
    
    
    // 直接分配本地内存空间
    ByteBuffer byteBuffer = ByteBuffer.allocateDirect(BUFFER);
    System.out.println("直接内存分配完毕,请求指示!");

    Scanner scanner = new Scanner(System.in);
    scanner.next();

    System.out.println("直接内存开始释放!");
    byteBuffer = null;
    System.gc();
    scanner.next();
  }
}

Después de ejecutar, verifique el PID de acuerdo con el comando jps, y podrá ver el uso de memoria específico en el administrador de tareas

> jps
20192 BufferTest

imagen-20220611235743380

Puede ver que ocupa más de 1G de espacio de memoria, solo ingrese una cadena de texto

imagen-20220611235902834

Puede ver que el uso de la memoria se ha reducido a 18,3 M

Razones para usar la memoria directa

Para leer y escribir archivos, debe interactuar con el disco y también debe cambiar del modo de usuario al kernel. Cuando el núcleo es demasiado grande, se requiere el funcionamiento de la memoria como se muestra en la figura a continuación.Usando IO, se requieren dos copias de memoria para almacenar datos duplicados, lo cual es ineficiente.

imagen-20220612000108805

Al usar NIO, como se muestra en la figura a continuación, el código Java puede acceder directamente al área de búfer directa dividida por el sistema operativo, y solo hay una copia. NIO es adecuado para operaciones de lectura y escritura en archivos de gran tamaño.

imagen-20220612000438494

Ejemplo de código:

/**
 * io与nio例子
 */
public class BufferTest1 {
    
    

  private static final String FILE = "D:\\迅雷下载\\新建文件夹\\XXX.mkv";
  private static final int _10Mb = 1024 * 1024 * 10;

  public static void main(String[] args) {
    
    
    long sum = 0;
    String src = FILE;
    for (int i = 0; i < 3; i++) {
    
    
      String dest = "D:\\迅雷下载\\新建文件夹\\XXX" + i + ".mkv";
      sum += directBudder(src, dest); // 总花费时间为:31345
//      sum += io(src, dest);         // 总花费时间为:62520
    }
    System.out.println("总花费时间为:" + sum);
  }

  private static long directBudder(String src, String dest) {
    
    
    long start = System.currentTimeMillis();
    FileChannel inChannel = null;
    FileChannel outChannel = null;
    try {
    
    
      inChannel = new FileInputStream(src).getChannel();
      outChannel = new FileOutputStream(dest).getChannel();

      ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_10Mb);
      while (inChannel.read(byteBuffer) != -1) {
    
    
        byteBuffer.flip(); // 修改为读数据模式
        outChannel.write(byteBuffer);
        byteBuffer.clear(); // 清空
      }
    } catch (IOException e) {
    
    
      e.printStackTrace();
    } finally {
    
    
      if (inChannel != null) {
    
    
        try {
    
    
          inChannel.close();
        } catch (IOException e) {
    
    
          e.printStackTrace();
        }
      }
      if (outChannel != null) {
    
    
        try {
    
    
          outChannel.close();
        } catch (IOException e) {
    
    
          e.printStackTrace();
        }
      }
    }
    long end = System.currentTimeMillis();

    return end - start;
  }

  private static long io(String src, String dest) {
    
    
    long start = System.currentTimeMillis();

    FileInputStream fis = null;
    FileOutputStream fos = null;
    try {
    
    
      fis = new FileInputStream(src);
      fos = new FileOutputStream(dest);
      byte[] buffer = new byte[_10Mb];
      while (true) {
    
    
        int len = fis.read(buffer);
        if (len == -1) {
    
    
          break;
        }
        fos.write(buffer, 0, len);
      }
    } catch (IOException e) {
    
    
      e.printStackTrace();
    }
    long end = System.currentTimeMillis();
    return end - start;
  }
}

Conclusión: en el caso de un tamaño de video de 1.5G, io toma 62520, nio toma 31345 y nio es más rápido

Características de la memoria directa:

  • También puede causar la excepción OutOfMemoryError
  • Dado que la memoria directa está fuera del almacenamiento dinámico de Java, su tamaño no estará directamente limitado por el tamaño máximo del almacenamiento dinámico especificado por -Xmx, pero la memoria del sistema está limitada, y el almacenamiento dinámico de Java y la memoria directa siguen estando limitados por el tamaño máximo que sistema operativo puede dar.Memoria.
  • defecto:
    • Los costos de asignación y recuperación son altos
    • No sujeto a la gestión de recuperación de memoria JVM
  • El tamaño de la memoria directa se puede establecer mediante -XX:MaxDirectMemorySize
  • Si no se especifica, el valor predeterminado es consistente con el valor máximo del parámetro heap -Xmx.

Ejemplo de código OOM de memoria directa

/**
 * 本地内存OOM OutOfMemoryError: Direct buffer memory
 */
public class BufferTest2 {
    
    
  private static final int BUFFER = 1024 * 1024 * 20; // 20MB

  public static void main(String[] args) {
    
    
    ArrayList<ByteBuffer> list = new ArrayList<>();

    int count = 0;
    try {
    
    
      while (true) {
    
    
        // 使用直接内存
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(BUFFER);
        list.add(byteBuffer);
        count++;
        try {
    
    
          Thread.sleep(100);
        } catch (InterruptedException e) {
    
    
          e.printStackTrace();
        }
      }
    }finally {
    
    
      System.out.println(count);
    }
  }
}

resultado de la operación:

362
Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
	at java.nio.Bits.reserveMemory(Bits.java:694)
	at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)
	at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)
	at BufferTest2.main(BufferTest2.java:19)

Ejemplo de código UnSafe OutOfMemoryError

/**
 * -Xmx20m -XX:MaxDirectMemorySize=10m
 */
public class MaxDirectMemorySizeTest {
    
    

  private static final long _1MB = 1024 * 1024;

  public static void main(String[] args) throws IllegalAccessException {
    
    
    Field unsafeField = Unsafe.class.getDeclaredFields()[0];
    unsafeField.setAccessible(true);
    Unsafe unsafe = (Unsafe) unsafeField.get(null);
    while (true) {
    
    
      unsafe.allocateMemory(_1MB);
    }
  }
}

resultado de la operación:

Exception in thread "main" java.lang.OutOfMemoryError
	at sun.misc.Unsafe.allocateMemory(Native Method)
	at MaxDirectMemorySizeTest.main(MaxDirectMemorySizeTest.java:17)

Conclusión: la memoria directa también será OOM

imagen-20220612142336384

Comprensión sencilla:

memoria de proceso java = montón java + memoria nativa

Supongo que te gusta

Origin blog.csdn.net/weixin_43811294/article/details/125245298
Recomendado
Clasificación