什么,系统内存溢出了?记一次JVM调优实战

2022-05-05 17:22:51 浏览数 (2)

这里我们不讲JVM的内存划分,垃圾判定算法,垃圾回收算法,垃圾收集器等知识。主要讲的是实际调优的操作,对JVM调优感兴趣的可以看下去。至于垃圾回收算法,可以看看我这篇文章:

“垃圾回收算法

公司系统出现内存溢出的故障,下面是内存溢出排除过程,我采用伪代码模拟了生产环境。

代码语言:javascript复制
public class MemoryLeakService {

    public List<User> distinct() throws InterruptedException {
        List<User> list = new ArrayList<>();
        CountDownLatch downLatch = new CountDownLatch(1);
        new Thread(() -> {
            try {
                for (int j = 0; j < 100; j  ) {
                    for (int i = 0; i < 100000; i  ) {
                        User user = new User(String.valueOf(i));

                        if (!list.contains(user)) {
                             list.add(user);
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                downLatch.countDown();
            }
        }).start();
        downLatch.await();
        return list;
    }
    public static void main(String[] args) throws InterruptedException {
        System.out.println(Thread.currentThread());
        new Thread().start();
        new MemoryLeakService().distinct();
    }
}

这里主要是模拟一个缓存加载的过程,将用户数据加载进List集合中。为了体现效果,我们将堆内存调小,并将内存溢出的堆栈信息打印出来,具体指令如下:

代码语言:javascript复制
-Xmx8m -XX: HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/temp/20200824.hprof

当运行系统时,出现如下报错

我们发现Thread-1的堆空间内存溢出了,并且生成如下文件20200824.hprof

打开内存分析工具 Eclipse Memory Analyzer

加载20200824.hprof文件,找到有问题的堆栈信息

控制台打印显示Thread-1由内存溢出,我们进Thread-1看

点击箭头处按钮

选择线程明细

我们发现MemoryLeakService第24行代码有问题,看看第24行代码

是这个对象导致的。

仔细看代码,发现User对象存入了List集合中

代码语言:javascript复制
if (!list.contains(user)) {
 list.add(user);
}

看看contains()源码

发现这里是比较的地址值,那!list.contains(user)永远为true。这里就相当于List存了100000 * 100 = 10000000个User

我们需要重写Userequals方法

代码语言:javascript复制
@Override
public boolean equals(Object o) {
    if (this == o) {
     return true;
    }
    if (o == null || getClass() != o.getClass()) {
     return false;
    }
    User user = (User) o;
    return Objects.equals(id, user.id);
}

@Override
public int hashCode() {
 return Objects.hash(id, name, age);
}

再次运行,没有出现内存溢出了。

好了,上面就是一次简单的内存溢出查找的过程了,关于工具Eclipse Memory Analyzer的使用,自己可以去网上下载下来,练习使用下。说不定哪天你们的系统真的出现内存溢出,自己就有用武之地了。

0 人点赞