一、背景:
使用java spring boot ,实现linux 服务器 内存溢出情况。
二、方案
1、打包成war包,可以直接将war包部署在tomcat容器里
2、spring boot,打包成jar包。打的jar包,内置了tomcat,所以在服务器上,直接启jar包就行,没有必要放在tomcat容器里部署,在启动jar包时,可以配置线程池等。
这里用 spring boot,打包成jar。
2.1 、新建 项目
java 选择8
勾选spring web
Spring boot 版本 <version>2.7.14</version>
2.2 编写项目
OOMController.java
package com.example.testoom; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @RestController public class OOMController { @GetMapping("/memoryLeak") public String memoryLeak() { // 是否调用remove方法 boolean doRemove = false; // 加锁,让多个线程串行执行,避免多个线程同时占用内存导致的内存溢出问题 final Object lockObj = new Object(); // 开启20个线程 ExecutorService executorService = Executors.newFixedThreadPool(20); // 为了不重复使用线程,用Map标记一下已经已使用过的线程, Map<Long, Integer> threadIdMap = new ConcurrentHashMap<>(); // 循环向线程变量中设置数据 1024 * 1024 = 1M for (int i = 0; i < 20; i++) { executorService.execute(() -> { synchronized (lockObj) { Integer num = threadIdMap.putIfAbsent(Thread.currentThread().getId(), 1); if (num == null) { ThreadLocal<Byte[]> threadLocal = new ThreadLocal<>(); threadLocal.set(new Byte[1024 * 1024]); // 手工回收 System.gc(); try { // 调用GC后不一定会马上回收 Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()); } } ); } // 线程池中的任务全部执行完毕 executorService.shutdown(); return "ok"; } }
配置端口号,8083
application.properties
spring.mvc.view.prefix=classpath:/templates/ spring.mvc.view.suffix=.html server.port=8083
2.3 打jar包
(如果之前,大过,要先cleam清理一下)
jar包在target目录下
2.4 部署服务在服务器启动该jar包
1、先将jar包上传到服务器。
2、启动jar包
参数解释 nohup 表示不挂断的运行, 注意并没有后台运行的功能,用 nohup 命令可以使命令永久的执行, 和客户端没有任何关系 & 表示是后台运行 -Xms20M -Xmx20M -Xmn10M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./heapdump.hprof 表示为了发生OOM的时候会自动导出Dump文件
java -jar -Xms300M -Xmx300M -Xmn100M testOOM-0.0.1-SNAPSHOT.jar -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./heapdump.hprof &
3 请求接口
部署之后请求一下接口: http://192.168.1.3:8083/memoryLeak
三、使用jmeter压这个接口
加大线程对接口进行压测,通过jmap或Arthas监控项目运行过程中(cpu、内存、磁盘)等关键信息
四、案例分析
分析思路1:
1、jmap 常用参数:
jmap命令进行堆转储(heap dump)时,如果 Java 进程遇到内存溢出(OutOfMemoryError),jmap命令会自动停止并输出堆转储文件。
jmap -histo:pid:打印指定 Java 进程(pid)的内存使用情况摘要。 jmap heap pid:打印指定 Java 进程(pid)的堆(heap)信息。 jmap -dump:format=b,file=output.dump:生成堆转储快照(dump 文件),并指定文件格式(b 表示二进制输出)。 jmap -finalize:查看 finalize 执行队列,即等待垃圾回收的线程。
获取当前进程id
存储dump堆文件:
jmap -dump:live,format=b,file=heap_jvmpertest_2023081901.hprof 9557
从项目运行日志关键字:java.lang.OutOfMemoryError: Java heap space,当前项目出现了内存泄露的情况
2、使用 jstat -gcutil 线程ID 9557 查看
结果排查, FGC比较频繁,多半是有问题
分析思路2:
Arthas定位分析
java -jar arthas-boot.jar 9557
1、输入 dashboard 查看线程整体的运行情况,heap的消耗情况,以及运行时环境
2、 输入 heapdump命令 将 hprof 文件下载下来,通过mat工具进行分析
mat工具直接加载hprof文件
点击Histogram,列出内存中的对象,对象的个数及大小
发现出问题的对象为 class java.lang.Byte[]
1)Byte[] 占用了空间的 90%以上,基本可以断定为是 Byte[] 没有被回收导致的内存泄漏
2)点击【overview】页签下方的leaksuspects,打开leaksuspects页签,自动分析内存内存泄漏的原因,可以直接定位到Class和行数;
参考笔记:
https://www.cnblogs.com/wzl0916/p/12928704.html
https://blog.csdn.net/bookssea/article/details/124073742
https://blog.csdn.net/huangliangbao2009/article/details/129785174
标签:java,jar,线程,内存,服务器,jmap From: https://www.cnblogs.com/xfbk/p/17639215.html