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
Puede ver que ocupa más de 1G de espacio de memoria, solo ingrese una cadena de texto
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.
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.
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
Comprensión sencilla:
memoria de proceso java = montón java + memoria nativa