Serie de máquinas virtuales: desbordamiento de memoria OOM y soluciones

¡Continúe creando, acelere el crecimiento! Este es el primer día de mi participación en el "Nuggets Daily New Plan · June Update Challenge", haz clic para ver los detalles del evento

OutOfMemoryError (OOM para abreviar) es un dolor de cabeza para los programadores Este tipo de problema generalmente ocurre cuando el espacio de memoria se está agotando y no hay suficiente espacio para que el programa lo use. En los programas de Java, hay muchas razones para el desbordamiento de la memoria, entre las que se incluyen el desbordamiento de la memoria del montón, el desbordamiento directo de la memoria y el desbordamiento del metaespacio/área permanente.

desbordamiento de montón

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
复制代码

El desbordamiento del montón es el más común. El montón es un espacio importante en Java. Una gran cantidad de objetos en Java se asignan directamente en el montón (consulte la asignación de memoria ). Cuando una gran cantidad de objetos ocupan espacio de almacenamiento dinámico y están fuertemente referenciados, de modo que nunca se pueden reciclar, cuando la suma del tamaño de todos los objetos es mayor que -Xmxel valor especificado por el parámetro, se produce un desbordamiento.

Puede haber muchas razones para el desbordamiento, aquí están las razones comunes:

  • Hay demasiadas clases de código en el proyecto de servicio y el espacio de almacenamiento dinámico no es suficiente, y puede ocurrir un error de desbordamiento de almacenamiento dinámico al inicio.
  • Hay bucles o bucles infinitos en el código, lo que genera demasiados objetos de entidad.
  • Al consultar la base de datos, se consulta una gran cantidad de datos a la vez.
  • El parámetro -Xmx para el inicio del servicio se establece demasiado pequeño, que es el primer punto.

Por ejemplo, seguimos agregando objetos a ArrayList y no podemos reciclarlos, lo que resulta en un desbordamiento del montón.

/**
* 启动参数限制最大堆和最小堆:-Xms10m -Xmx10m
*/
public class Test2 {
    public static void main(String[] args) {
        ArrayList<byte[]> list = new ArrayList<>();
        for (int i=0;i<10;i++){
            list.add(new byte[1024*1024]);
        }
    }
}
复制代码

Error al iniciar:

com.ajisun.coding.ajisunmybatis.Test2
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
  at com.ajisun.coding.ajisunmybatis.Test2.main(Test2.java:23)
复制代码

El error Java heap spaceindica un desbordamiento de la memoria del montón.

Como lidiar con

  • Es preferible comprobar si el código tiene bucles o bucles infinitos y si puede crear objetos continuamente.
  • Compruebe si los parámetros de inicio -Xmxy -Xmsla memoria del montón establecida son demasiado pequeños para cargar todas las clases en el servicio y puede aumentarlos adecuadamente.
  • Compruebe si hay una consulta de base de datos en el código, sin paginación para devolver una gran cantidad de datos a la vez.
  • También puede analizar a través de las herramientas MAT o VisualVM para encontrar objetos que ocupan mucho espacio de almacenamiento dinámico y luego realizar optimizaciones razonables.

desbordamiento directo de memoria

Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
复制代码

Este problema generalmente se encuentra relativamente raramente y la memoria directa no es parte del área de datos de tiempo de ejecución.

Java中NIO(New IO)是支持直接使用直接内存的,可以直接获取一块堆外空间使用,而这块空间是直接向操作系统申请的。直接内存的申请速度一般比堆内存慢,但是其访问速度要快于堆内存,所以如果存在可复用且经常被访问的空间,使用直接内存可以提高系统的性能。但是直接内存没有被Java完全托管,使用不当容易出现溢出的问题。

Java中的DirectByteBuffer就是直接内存申请的,使用unsafe的native方法allocateMemory

配置启动参数-XX:MaxDirectMemorySize设置直接内存的最大值,如果不配置此参数则默认最大可用直接内存是-Xmx的值

/**
* 启动参数:-XX:MaxDirectMemorySize=5m
*/
public class Test2 {
    public static void main(String[] args) {
    ByteBuffer bb = ByteBuffer.allocateDirect(1024*1024*6);
    }
}
复制代码

运行出现错误Direct buffer memory

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)
复制代码

如何处理:

  • 检查程序中使用直接内存的代码是否恰当。
  • 检查参数-Xmx和-XX:MaxDirectMemorySize 的大小是否合理,可以根据实际情况调整其大小。

线程过多导致OOM

Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
复制代码

这个错误是程序中创建的线程过多,而线程需要的空间却不够了。

我们运行程序创建的线程都是需要一定内存的,而机器的内存大小是一定的,当线程的数量过多的时候,就会导致OOM。

如下用死循环不断的创建新的线程。

public class Test2 {
    public static void main(String[] args) {
        while(true){
            new Thread(new Runnable(){
                @Override
                public void run() {
                    try {
                        Thread.sleep(10000000);
                    } catch(InterruptedException e) { }
                }
            }).start();
        }
    }
}
复制代码

运行出现异常,说明系统创建的线程数量已经到达最大值。

Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
  at java.lang.Thread.start0(Native Method)
  at java.lang.Thread.start(Thread.java:717)
  at com.ajisun.coding.ajisunmybatis.Test2.main(Test2.java:39)
复制代码

如何处理:

  • 首先检查代码,是否有优化的空间,减少不必要的线程创建,或者使用线程池复用线程。
  • 减少堆空间的内存,这样操作系统可以预留更多的内存用于线程创建。
  • 减少每个线程内存空间的占用,使用-Xss可以指定栈空间的大小(注意:太小会出现栈溢出)。
  • 如果真的需要的线程特别多,但是受到操作系统的限,这种需要修改操作系统的最大线程数。

永久代/元空间溢出

Java.lang.outofMemoryError:PermGen Space( 1.8之前)

或者

java.lang.OutOfMemoryError: Metaspace( 1.8之后)

这种错误是永久代或者元空间溢出,在jdk1.8之前会出现这种错误,之后hotspot用元空间代替了永久代来存储class信息。如果一个系统在不断的创建新的类(不是对象实例),那么最终会导致元空间溢出的。

如何处理:

  • 增加元空间的大小,设置其对应参数的值 -XX:MaxMetaspaceSize=512m
  • 减少系统需要的类的数量,检查是否有不需要的类并且清除掉。
  • 使用ClassLoader合理的装载各个类,并定期进行回收。

GC效率低下引起的OOM

java.lang.OutOfMemoryError:GC over head limit exceeded
复制代码

这个错误比较少见。

Java中主要特点就是垃圾回收,而GC是内存回收的关键,如果效率低下,会严重影响系统的整体性能。如果系统的堆空间太小,那么GC所占的时间就会比较多,并且回收所释放的内存也比较少。根据GC占用系统时间以及内存释放的大小,可以评估出GC的效率,一旦虚拟机认为GC的效率过低,就可能直接抛出OOM异常。

一般情况下,虚拟机会检查以下几种情况来判断效率是否低下:

  • 花在GC上的时间是否超过了98%。
  • 老年代释放的内存是否小于2%。
  • eden 区释放的内存是否小于2%。
  • 是否连续最近5次GC都出现了上述情况(是同时出现,不是出现一个)。

只有满足上面的所有条件才会出现上面的OOM

如何处理:

  • 可以通过开关-XX:-UseGCOverheadLimit禁止OOM的产生。这样就会出现堆溢出的错误,然后根据堆溢出的问题来处理。
  • 优化代码,检查新生代,老年代中对象是否正常,是否有过多的对象无法释放。
  • dump内存,检查是否存在内存泄露,如果没有,加大内存。

本文讲解了几种内存溢出的情况,以及相对应的解决思路。其中常见的错误是堆溢出和线程过多的OOM。

原文地址


我是纪先生,用输出倒逼输入而持续学习,持续分享技术系列文章,以及全网值得收藏好文,欢迎关注公众号,做一个持续成长的技术人。

个人网站

JVM虚拟机系列历史文章

1. 虚拟机系列:jvm运行时堆内存如何分代;

2. 虚拟机系列:jvm中的垃圾回收算法;

3. 虚拟机系列:jvm运行时数据区域;

4. 虚拟机系列:JVM中对象的创建,内存布局和访问定位;

5. 虚拟机系列:JVM中的垃圾收集器;

6. Serie de máquinas virtuales: asignación de memoria en JVM;

7. Serie de máquinas virtuales: comprenda los registros y los parámetros de registro de las máquinas virtuales;

8. Serie de máquinas virtuales: herramientas básicas para la supervisión del rendimiento de máquinas virtuales;

9. Serie de máquinas virtuales: herramienta básica para el monitoreo del rendimiento de máquinas virtuales-jstat;

10. Serie de máquinas virtuales: herramienta de visualización de monitoreo de rendimiento - JConsole;

11. Serie de máquinas virtuales: herramienta de monitoreo gráfico-VisualVM;

Supongo que te gusta

Origin juejin.im/post/7102976491608080392
Recomendado
Clasificación