当前位置:网站首页>一次内存泄露排查小结
一次内存泄露排查小结
2022-08-03 11:57:00 【freesOcean】
问题重现
前段时间给DSP(实际竞价广告投放系统)系统开发了一个前置数据去重处理服务,开始两天没有问题,但是第三天去看数据,发现进程不在。
查看阿里云机器负载,发现前两天内存持续在高位,初步怀疑内存溢出,导致异常退出。
排查思路
1.确定代码中有大量对象占用的地方,着重排查:因为堆内存是JVM中内存占用最大的一块。
2.多线程代码排查:多线程下容易出现隐蔽的bug
3.是否是机器本身内存太小,有其他进程占用大量内存。
关于思路1:要清楚使用的jdk版本,以及相应版本对应的JVM内存的分布情况。
要清楚以下几点:
首先JVM内存受限于实际的最大物理内存,JVM内存的最大值跟操作系统有很大的关系。简单的说就32位处理器虽然可控内存空间有4GB,但是具体到JVM操作系统会给一个限制,这个限制一般是2GB-3GB(一般来说Windows系统下为1.5G-2G,Linux系 统下为2G-3G),而64bit以上的处理器就不会有限制了。
JVM初始分配的内存由-Xms指定,默认是物理内存的1/64;JVM最大分配的内存由-Xmx指定,
默认是物理内存的1/4。默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;
空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制。因此服务器一般设置-Xms、 -Xmx相等以避免在每次GC后调整堆的大小。
JDK8后,方法区在元空间实现,元空间使用直接内存. 但是一般方法区占用内存并不会太大。
优化方法
如果进程已经挂掉,可以分析堆内存快照,具体设置方式参考下文。如果进程还在,可以查看当前的内存情况,排查问题:
通过java自带的工具查看堆内存情况
#查看堆内存使用情况 jmap -heap pid Heap Usage: PS Young Generation Eden Space: capacity = 290455552 (277.0MB) used = 290455552 (277.0MB) free = 0 (0.0MB) 100.0% used From Space: capacity = 20447232 (19.5MB) used = 14548992 (13.875MB) free = 5898240 (5.625MB) 71.15384615384616% used To Space: capacity = 20447232 (19.5MB) used = 0 (0.0MB) free = 20447232 (19.5MB) 0.0% used PS Old Generation capacity = 420478976 (401.0MB) used = 364028688 (347.16481018066406MB) free = 56450288 (53.83518981933594MB) 86.5747656310883% used
这里需要了解JVM垃圾回收器的分代逻辑,简单来说就是新创建的对象放在Eden Space, 之后将存活的对象放入From Space和To Space区域,如果超过一定次数,则放入Old Generation,所以如果老年代如果空间一直上升,而没有下降,则说明有内存泄露,导致对象一直没有释放,就要重点排查。具体可以结合dump文件。
通过java自带工具jmap查看class实例等信息,作用类似于查看dump文件。
#输出当前class的实例数目、内存占用、类全名信息。 [root@iZ0xicvgqrspr6w1sxojjeZ tmp]# jmap -histo:live 6104 | head -n 20 num #instances #bytes class name ---------------------------------------------- 1: 66065 10737048 [C 2: 12932 9308496 [B 3: 12049 4969152 [I 4: 28079 2562408 [Ljava.lang.Object; 5: 65697 1576728 java.lang.String 6: 48265 1544480 java.util.concurrent.ConcurrentHashMap$Node 7: 39782 1273024 java.util.HashMap$Node 8: 11210 1239576 java.lang.Class 9: 1036 1064000 [Lio.netty.util.Recycler$DefaultHandle; 10: 15295 611800 java.security.cert.TrustAnchor 11: 5817 511896 java.lang.reflect.Method 12: 1602 493712 [Ljava.util.concurrent.ConcurrentHashMap$Node; 13: 3865 485632 [Ljava.util.HashMap$Node; 14: 20168 484032 java.util.ArrayList 15: 27529 440464 java.lang.Object 16: 8681 347240 java.util.LinkedHashMap$Entry 17: 390 255840 io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueue
通过jstat命令查看gc情况
如果FGC次数比较频繁,则说明内存吃紧,要排查是内存本身较小,还是内存泄露。
[[email protected] work]# jstat -gc 28686 S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 8192.0 7680.0 4544.0 0.0 770560.0 497923.9 1310720.0 852555.0 65624.0 62895.9 8024.0 7467.0 146565 2120.377 132 84.347 2204.724
S0C:年轻代中第一个幸存区的大小
S1C:年轻代中第二个幸存区的大小
S0U:年轻代中第一个幸存区的使用大小
S1U:年轻代中第二个幸存区的使用大小
EC:年轻代中伊甸园区的大小
EU:年轻代中伊甸园区的使用大小
OC:老年代大小
OU:老年代使用大小
MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
YGC:年轻代gc次数
YGCT:年轻代消耗时间
FGC:老年代gc次数
FGCT:老年代gc消耗时间
GCT:gc消耗总时间
查看内存占用靠前的java进程
top -o %MEM -b -n 1 | grep java | awk '{print "PID: "$1" \t MEM: "$6" \t %CPU: "$9"% \t %MEM: "$10"%"}'
到处堆内存快照
jmap -histo [进程id] > jmap.txt
首先调整java运行时的相关参数,包括内存溢出时的堆内存快照。
nohup java -Xmx2048m -Xms2048m -Xmn768m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof -jar ${ jar_name} --spring.profiles.active=prod-us --server.port=8089 >/dev/null 2>&1 &
JVM参数 描述 默认 推荐 -Xms Java堆内存的最小值,即初始值 OS内存1/64 OS内存一半 -Xmx java堆内存最大值 OS内存1/4 OS内存一半 -Xmn 堆内存新生代大小,扣除新生代
就是老年代大小默认堆的1/3 sun推荐3/8 -Xss 每个线程的栈内存大小 和jdk有关 512k~1M 如果是本身内存设置过小,一般通过调整参数便可以解决。如果是其他问题,可以通过heapdump.hprof文件协助查看占用内存较大的对象分布,方便排查。
根据这些信息,重点排查相关部位的代码,一般都能解决。之前我还遇到在使用线程池时,采用了Executors工具类去创建线程池,而用它创建线程池,默认的等待队列是无界队列,所以有可能导致创建大量等待线程,导致内存溢出(线程要占用资源)。正确的做法是使用下面这种方式,给一个有界队列作为参数。
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, new LinkedBlockingQueue<>(2000), new DefaultThreadFactoryA(), new ThreadPoolExecutor.CallerRunsPolicy());
查看java进程常用命令:
#1.简洁信息:可以查看进程号 jps #2.可查看进程号和jar包名称 jcmd jps -lv #3.详细信息 ps -ef|grep java
查看系统内存情况:
#Top命令 ,可以查看系统负载情况,内存占用,和进程的情况 top - 18:59:23 up 5 days, 8:28, 2 users, load average: 28.50, 30.68, 30.82 Tasks: 84 total, 1 running, 83 sleeping, 0 stopped, 0 zombie %Cpu(s): 0.3 us, 0.3 sy, 0.0 ni, 99.3 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st KiB Mem : 8008928 total, 3534996 free, 3400088 used, 1073844 buff/cache KiB Swap: 0 total, 0 free, 0 used. 4355048 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 1150 root 10 -10 140104 18748 11464 S 1.3 0.2 90:48.01 AliYunDun 947 root 20 0 1354476 21268 8416 S 0.3 0.3 29:38.53 /usr/local/clou 1 root 20 0 43584 3976 2620 S 0.0 0.0 0:04.72 systemd 2 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kthreadd 4 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kworker/0:0H 5 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kworker/u4:0 6 root 20 0 0 0 0 S 0.0 0.0 0:35.24 ksoftirqd/0 7 root rt 0 0 0 0 S 0.0 0.0 0:00.44 migration/0 8 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcu_bh 9 root 20 0 0 0 0 S 0.0 0.0 1:27.46 rcu_sched
只想查看java进程的情况:
[[email protected] ~]# top -o %MEM -b -n 1 | grep java | awk '{print "PID: "$1" \t MEM: "$6" \t %CPU: "$9"% \t %MEM: "$10"%"}' PID: 1592 MEM: 2.4g %CPU: 0.0% %MEM: 31.4% PID: 1506 MEM: 383856 %CPU: 0.0% %MEM: 4.8%
知识小结
内存泄露、JVM调优等问题一般比较隐蔽,首先要对jvm的相关知识有一个大体认知,其次要熟悉java中相关工具类的正确用法,最后就是要有给力的工具,比如jprofiler等等。
边栏推荐
- LeetCode-1796. 字符串中第二大的数字
- Vs Shortcut Keys---Explore Different Programming
- 从零开始Blazor Server(6)--基于策略的权限验证
- ROS中编译通过但是遇到可执行文件找不到的问题
- C language advanced article: memory function
- LeetCode 899 有序队列[字典序] HERODING的LeetCode之路
- 深度学习跟踪DLT (deep learning tracker)
- 学习软件测试需要掌握哪些知识点呢?
- 用C语言解决A+B问题,A-B问题,A*B问题
- Redis发布订阅和数据类型
猜你喜欢
一个扛住 100 亿次请求的红包系统,写得太好了!!
Matlab学习12-图像处理之图像增强
How to do App Automation Testing?Practical sharing of the whole process of App automation testing
笔试题:金额拆分
4500字归纳总结,一名软件测试工程师需要掌握的技能大全
ROS中编译通过但是遇到可执行文件找不到的问题
Matlab学习10-图像处理之傅里叶变换
4500 words sum up, a software test engineer need to master the skill books
基于Sikuli GUI图像识别框架的PC客户端自动化测试实践
深入理解MySQL事务MVCC的核心概念以及底层原理
随机推荐
代码分析Objective-C中的深拷贝与浅拷贝
dataset数据集有哪些_数据集类型
After completing the interview and clearance collection of Alibaba, I successfully won the 15th Offer this year
深入理解MySQL事务MVCC的核心概念以及底层原理
基于Sikuli GUI图像识别框架的PC客户端自动化测试实践
LeetCode 899 有序队列[字典序] HERODING的LeetCode之路
-树的高度-
一文带你弄懂 CDN 技术的原理
Realize 2d characters move left and right while jumping
什么是bin文件?「建议收藏」
劝退背后。
基于Sikuli GUI图像识别框架的PC客户端自动化测试实践
数据库一席谈:打造开源的数据生态,支撑产业数字化浪潮
fastposter v2.9.0 程序员必备海报生成器
ThreadLocal源码解析及使用场景
App自动化测试怎么做?实战分享App自动化测试全流程
一个扛住 100 亿次请求的红包系统,写得太好了!!
I in mother's womb SOLO20 years
MySQL之json数据操作
FR9811S6 SOT-23-6 23V, 2A Synchronous Step-Down DC/DC Converter