OutOfMemoryError GC overhead limit exceeded

今天在代码调试过程中,在通过swagger调试某个接口(restful api)的时候,遇到了该错误:OutOfMemoryError GC overhead limit exceeded 

通过百度之后,知道该错误产生的原因是:jvm的垃圾回收期回收效率太低,并且多次触发gc之后,还是无法有效回收内存。

显然,该错误是我的代码中,迅速且持续不断的产生新的对象,导致jvm一直gc,并且每次gc过后,又有一堆新的对象产生了,故而又重新触发gc,如此反复循环,jvm一直忙于gc,电脑的cpu和内存使用率一直居高不下,几乎都跑满100%,系统非常的卡顿,无法处理其他事情,这个系统一直维持着这样一种超负荷状态,但就是不会奔溃,反而是我要奔溃了。

分析原因过程:

  一开始是,打算用jvm内存分析工具,来查看问题,比如plumbr, VisualVM, JConsole等,但是第一次用不熟悉,只能看到cpu和内存波动很大 ,无法有效定位问题。

  现在先撇开工具,

  ……

  我猜想是,代码中出现了无限递归、死循环或者死锁的情况。

  首先是无限递归,这种情况应该可以排除,因为这种情况已出现的话,系统是直接奔溃,报错应该是 stackOverFlow。 

  然后是死锁,这种情况是多线程的调度问题,而我写的代码里面,没有涉及到线程的调度,估计也不是这种情况。

  最后就剩下死循环的情况了。

  ……

  经过了几个世纪的挣扎,突然灵光一闪,会不会是之前加入的接口数据返回格式统一配置这里,出了问题,在解析返回数据时,出现了死循环。

  看了下代码:

  

public static List<Field> getAllFields(Object object) {
        List<Field> allFields = new ArrayList<>();
        Class<?> aClass = object.getClass();

        allFields.addAll(Arrays.asList(aClass.getDeclaredFields()));

        while (aClass.getSuperclass() != Object.class && aClass.getSuperclass() != null) {
            aClass = object.getClass().getSuperclass();
            allFields.addAll(Arrays.asList(aClass.getDeclaredFields()));
        }
        return allFields;
    }

  终于找到问题原因所在了,自己写的方法,有个严重bug:对象的类型(aClass)迭代无效,一直是同一个。修改代码如下即可:

public static List<Field> getAllFields(Object object) {
        List<Field> allFields = new ArrayList<>();
        Class<?> aClass = object.getClass();

        while (aClass != Object.class && aClass != null) {
            allFields.addAll(Arrays.asList(aClass.getDeclaredFields()));

            aClass = aClass.getSuperclass();
        }
        return allFields;
    }

以此为戒,以后写过的函数,要经过仔细的测试,尤其涉及到循环和递归的东西,更要慎重,注意其结束条件。

原文地址:https://www.cnblogs.com/zhangxuezhi/p/11763441.html