Java OOM 怎么排查

很多Java开发新手遇到程序突然卡住、服务器报“内存溢出”的红错时,第一反应是慌——不知道哪块代码“吃内存”,也不知道从哪下手找问题。其实Java内存溢出(OOM,很多人会叫它“内存崩了”)的排查根本不用复杂的高深工具,跟着生活化的思路一步步来就能搞定,咱们用租房的例子来拆解,保证你一看就懂。

先搞懂:Java内存是什么?

可以把Java程序的内存当成你租的出租屋,屋里有不同区域放不同的东西:小衣柜放常用的衣服(叫“栈内存”,用完就扔,换衣服直接塞),大储物间放长期不用的杂物(叫“堆内存”,平时写的类对象都放这),还有玄关抽屉放临时记录的便签(叫“方法区”,存类的信息、常量这些)。

Java OOM 怎么排查

所谓OOM,就是“堆内存”或其他区域的空间被堆满了,房东(JVM)不让你再放东西,程序就崩了。排查的核心就是:看储物间里到底堆了什么杂物,为什么拿不走。

第一步:先抓“现场”,别让内存被清了

程序刚崩的时候,别重启服务器!就像家里漏水刚淹了地板,先别收拾,先拍现场,不然水干了就看不出哪漏的。

JVM自带一个救命的工具叫jmap,相当于给储物间拍张全景照:敲一行简单的命令就能把堆里所有对象的数量、大小列出来,比如能看到“有1000个User对象,每个占1KB”,一目了然。

如果没提前开这个工具,程序重启后内存清空,就只能靠服务器的日志碰运气了——所以平时写服务时,最好提前加个配置:程序崩的时候自动拍堆的快照,留好“尸体”再重启,这是排查的基础。

第二步:找“元凶”,从数量和大小入手

拿到堆快照后,不用抠每一个小对象,先看两个关键点:

第一,什么对象最多?比如你开了个批量导入用户的功能,结果代码里写了个循环,每次循环都new一个User对象,没有把用过的User删掉,就像你每次换衣服都把旧衣服塞床底,不扔也不整理,床底很快堆满。这时候堆快照里User对象的数量会比正常情况多几十倍,直接就能锁定这个功能有问题。

第二,什么对象最大?比如你有个上传大文件的功能,把整个10G的文件都塞进了堆里,而JVM给的堆内存只有5G,那肯定溢出。这时候快照里会看到有个对象占了8G内存,一查就知道是文件没处理好,应该用“流”一点一点读,不是一次性全塞进去。

第三步:验证猜想,用小测试确认

找到疑似的对象后,别先急着改代码,先验证是不是真的导致OOM的原因。比如你怀疑某个循环的对象没释放,就写个小测试:在本地把这个循环跑100次,看内存是不是涨上去,跑1000次会不会直接崩,要是和线上的情况一致,那这个循环就是元凶。

还有一种特殊情况:不是对象多,是对象“活着”没人用——就像你把旧衣服塞床底,但是衣柜的门没关,你总觉得还能穿,就一直不扔。这种情况叫“内存泄漏”,不是堆里的对象全没用,是不该活着的还活着,工具jvisualvm(JVM自带的可视化工具)能帮你标出来“哪些对象没人用还占着内存”,一找一个准。

最后:别踩的小坑

很多新手排查时会犯两个错:一是重启了再找,现场没了白忙活;二是只看日志里的错误提示,不看具体数据——日志只会说“内存溢出了”,不会说“是User对象太多导致的”。只要跟着“拍现场→找异常对象→验证”的步骤来,不管是批量导入、上传大文件还是第三方插件的内存泄露,都能很快搞定。

上一篇 C / C++ 编程语言核心特性与应用场景的知识科普