启动参数如下
java -server -Xms2048m -Xmx2048m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/xxx/heapError -jar xxx.jar --spring.profiles.active=prod --server.port=9551

项目启动一周后内存就已经 2.6g 了。。。也没有出现过 oom 异常,请教大家应该如何排查解决呢?

jdk版本如下
java version "1.8.0_361"
Java(TM) SE Runtime Environment (build 1.8.0_361-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.361-b09, mixed mode)

堆 dump 看看

dump 下来去分析内存

找算命先生测算一下

超出堆内存大小,应该就是堆外内存的问题了。用上面兄弟的 jmap dump 出来看看。但要注意会触发 FGC 。

xmx 限制了,那就是堆外泄露了呗

没挂能跑就行,管它呢。配置的 Xmx 只是堆内存大小,jvm 自己使用的内存,文件句柄啊,线程本身内存啊的都不在这里。

蹲一下解决方案,我有个开发的应用也是这样,时间越长内存越高,慢慢稳定在约束的最大内存,永远不会自动是释放内存,推测是写代码习惯问题,导致对象得不到释放

arthas 在线看一下内存占用情况

我们公司现在使用 消息中间件 nats 也有这种问题 😅

管他呢,搞个定时重启完事

查查有没有静态的集合,一般这种稳定增长的就是静态 map 或者 list 越来越大。不过我也建议写个定时重启算了。

这个得看你的项目有多大,小项目内存持续增大,大概率是代码问题,没有及时释放内存,需要看 dump

巧了,我写的其中一个 netty 也是这样,后来实在找不到问题了,每两个月重启一下
每天重启下就好了,java 就这样的

巧了,我写的其中一个 netty 也是这样,后来实在找不到问题了,每两个月重启一下Netty 大量使用了堆外内存。

hprof

启动参数加上-XX:NativeMemoryTracking=summary ,然后 jcmd pid VM.native_memory 分析下

巧了, 我也遇到过这个问题, 不过增长的慢, 离职了都没想着去解决这个问题. 蹲下排查方法

别理解错了,xmx 只限制了堆的最大值,还有非堆,线程栈,代码缓存,jvm 本身运行所需要的内存等,如有必要可以配置 NMT 进行分析,但是注意有性能损耗

mark 等个大佬,公司项目也是,看 arms 堆内存+非堆不到占用的内存,怀疑堆外又没办法排查,最后只能重启

netty 这种应用内存就是跟着连接数涨的,吃的是堆外内存,一条连接一个读写缓冲区。gc 管不着

巧了,我们把网关升级到 spring gateway 之后,也出现这问题,就是 netty 申请的堆外内存导致的。

2.6G 内存,超过堆内存的上限 2G ,堆外内存泄漏了。先 smaps 看下堆内存占用多少,算出堆外内存占用。堆外内存如果没特殊配置,最大也只能占 2G ,可能很快就 OOM 了。堆外内存很难定位泄漏点,给一个参考: tech.meituan.com/2018/10/18/netty-direct-memory-screening.html实在不行只能把内存用 gdb dump 出来,然后强行查看里面的内容,推测可能的泄漏原因。

#23 你说的这个,又是 smaps ,又是 gdb ,大部分的 java 开发不懂

不是人均阿里 P7 P8 水平,大部分还是 CRUD

#24 不过还是可以学到知识

哈哈哈 我的是内存正常 但是十几天后占用内存变小 随后宕机 问题是内存泄露 高并发的读取数据库导致有些线程超时未能关掉 好烦 不多线程的话 方法执行完需要三分多钟 现在还没想到长期运行的办法

#24 想问下排查内存泄漏这种问题对于 Java 开发是什么层次。曾经解决过公司一个服务的内存泄漏,堆外问题确实很难定位到,从简单的 NativeMemoryTracking ,pmap 查看内存分配,监控栈函数调用,jeprof 对比堆内分配,最终定位到框架的问题,确实在对应版本 issue 中找到,升级后解决。

  • JVM 不是它需要多少内存,才占用多少内存;而是在 -Xmx 允许的前提下,只要你系统还有空余,它就会大方地申请占用;而且即使之前申请了,后续不需要这么多了,它也不会及时退还给 OS ; 你可以通过 jhsdb jmap --heap --pid ${jvm_pid} 来确认你的应用当前实际只 需要/占用 多少内存; 如果你希望让 JVM 需要多少内存,才占用多少内存,多占用了就及时释放,可以通过调整 -XX:MinHeapFreeRatio -XX:MaxHeapFreeRatio 来实现。

当然上面所说,前提是你的应用没有内存泄漏。

#24 对于一些非重要服务,楼上的低频时段重启确实简单暴力,排查内存泄漏问题起码两周起步

#28 P7 P8 我说的可能有点夸张了,但实际中大部分 Java 开发不会(不想浪费时间或者没有能力)这么排查,大部分就是扩容服务器、容器内存+定期重启解决

学到了

-Xms -Xmx 限制的是堆内存,JVM 不止有堆内存,JVM 内存大于 Xmx 是正常情况。堆内存只是用来存储对象的成员变量的,对象的方法运行期间使用的基本类型变量(包括数组),要占用栈内存,这些是随运行动态申请和释放的。如果是服务器负荷期间内存升高,无需理会,负荷降了就会自己下去。Java 类定义,类的成员方法的函数定义,类的静态变量,也是要占用内存的。这些内存通常是一旦加载就不会释放(具体取决于类加载器),这种情况会导致 JVM 启动一段时间后内存就略微增长。但这种情况也请无需理会,首先这是 JVM 的职责,其次就算它真是屎山你也不该去碰。

#32 我没有大厂经验,想了解下现在的行情。年后要重新找工作(允悲

堆外内存设置 JVM 管不着,项目迭代快就懒得管了(基本一两周就会上线一次),没崩就行

换 openj9

看内存快照,里面看下 dominator_tree 最大对象是不是有啥问题

很好奇,都说堆外内存泄露的,难不成都是用 jni 开发 Java 项目嘛?

你的 jdk 版本号多少,我遇到过 java8 低版本 bug ,会导致堆外内存一直涨

哥们,线程不占内存?

-XMX 定好了,那大概率是堆外,不确定你这是 RSS ,还是 COMMITED 的 2.6G ,建议开 NMT 然后看一下假如没有 native memory leak ,大概率是有线程泄漏了,导致内存泄漏了

#28 层次是和别人对比出来的例如你觉得自己底层厉害,想找基础框架的,那么和基础框架组的人对比,你的能力是什么范围,懂底层的多少东西,如果只会内存泄漏这一个,其他的不熟悉,那你定级就比较低。假如你 JVM 精通、线程模型精通、数据结构精通,网络精通,那又是另一个层面。排查问题是一种能力,根据知识来设计软件架构又是一种能力

我觉得你说的有道理。xmx 确实只限制堆内存。内存的问题就应该交给 JVM 处理。再怎么调优都比不过新的 jdk 带的垃圾收集器优先排查代码问题,再考虑 JVM 调优知乎的这篇文章可能对 OP 有帮助 zhuanlan.zhihu.com/p/432258798

#43 OK 了解,多谢回复,学习成长路漫漫。(后面几个精通给我干懵了

2.6G 如果不出现内存报警,要么你的 2.6G 不准,要么有堆外内存(比如 netty 可以创建堆外内存),同时也建议看看 gc 次数频繁不频繁你也说了是一周之后 2.6G,但是这个并没有什么参考,你需要看这一周的内存变化,来分析情况

起个定时任务强制 full gc ,都不用重启了/dog

先升级 jdk 版本看看

多对比几次 smaps 看堆外内存有没有持续上涨

笑死了,当初我们开发的一个项目,根据模板渲染 Excel 表格,里面的图片很多(五六十张吧),最后转成 PDF 导出,导致这个接口但凡调用一次就 fullgc 几次,最后找不出问题就写了个定时任务隔一段时间重启服务😂

#50 fullGC 频繁,代码不太烂的话,加内存就好了。(省心省力

没有监控吗。一般就是 dump 下来,使用 memory analyzer 分析一下

项目里面有自己用 netty 直接内存池进行分配和释放么?用了加-Dio.netty.leakDetection.level=PARANOID 参数,检测下 netty 侧内存有没有泄漏,隔段时间在日志里搜 LEAK 就行了。如果没有直接玩 netty 内存池,加 #17 提到的参数分析 NMT ,多收集几次,看下具体哪个区域在不断增大,收集更多的信息,另外最好把 jdk 版本贴出来,遇到过 openjdk 连续几个小版本反射导致内存泄漏,如果 jdk 小版本版本号太小,最好直接升级到小版本较高的版本

感谢 项目中没有用到 netty 我去加启动参数看看

我上面没有细看你的命令,直接断定你的程序存在直接内存泄漏,这其实不太对。又看了眼你的命令,堆内存给了 2G ,一周跑下来 2.6G ,其实感觉很可能是正常的,因为命令里只限制堆内存,其他区域并没有任何限制,至少把 MaxDirectMemorySize 、ReservedCodeCacheSize 、MaxMetaspaceSize 这几个参数填了。如果还限制不住,可以考虑增加-Djdk.nio.maxCachedBufferSize 、-DMALLOC_ARENA_MAX 、-DMALLOC_MMAP_THRESHOLD_这些参数的配置,并且给比较小的值,如果还是不断增长,再考虑内存泄漏。

而内存的总量保持不变。

是如何确认内存不断增大呢,通过 top 吗

老生常谈的问题了, k8s limit pod 的内存限制, 触发 OOM 就释放了,就是这么粗暴。

先 dump 简单分析下再看其它问题.

有 buffer 没释放掉

7* 24 后台线程不能在 循环里声明变量,否则内存会爆。

感觉没问题,还有堆外内存呢,Metadata ,Codecache 等。使用中 jit 会把热点字节码编译成机器码放到 codecache 中,这个增长不会一直持续。

Netty 的 buffer 是基于计数器来决定是否释放的,每个 handler 有义务对不再使用的 buffer 做减少引用的操作,继承了 SimpleChannelInboundHandler 的话会自动执行一次减少引用的行为,在 handler 链的末端会再次做一次减少引用的动作,如果 handler 链执行完,buffer 的引用计数为 0 就会被释放,所以有些业务需要会通过 ReferenceCountUtil.retain() 增加引用计数,避免 buffer 被释放,不小心会导致 buffer 泄露,实际上在 netty 中已经内置了泄露分析的工具,可以考虑在测试环境添加 io.netty.leakDetectionLevel 参数来分析是否存在 buffer 泄露。