启动参数如下
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)
在听取了 @qiubinren 55楼的建议后,我追加了几个jvm参数,目前项目的内存没有出现溢出的情况 新的启动参数如下:
java -server -Xms1024m -Xmx2048m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/xxx/heapError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC -jar /xxx.jar --spring.profiles.active=prod --server.port=1234
-Xms1024m: 设置JVM启动时的初始堆内存大小为1024MB。堆内存用于存储Java对象。
-Xmx2048m: 设置JVM最大堆内存大小为2048MB。这是JVM可以用于对象存储的最大内存限制。
-XX:MetaspaceSize=128m: 设置元空间的初始大小为128MB。元空间用于存储类元数据,并且是在Java 8中取代永久代(PermGen)的内存区域。
-XX:MaxMetaspaceSize=512m: 设置元空间的最大大小为512MB。这是元空间可以增长到的最大内存限制。
-XX:+HeapDumpOnOutOfMemoryError: 当JVM抛出内存不足错误(OutOfMemoryError)时,自动生成堆转储文件。这有助于事后分析内存问题。
-XX:HeapDumpPath=/xxx/heapError: 指定堆转储文件的保存路径。如果JVM抛出内存不足错误,转储文件将被保存在这个路径下。
-XX:+PrintGCDateStamps: 打印垃圾收集日志时附带时间戳,有助于分析垃圾收集发生的时间。
-XX:+PrintGCDetails: 打印详细的垃圾收集日志。这提供了关于垃圾收集活动的更多信息。
-XX:NewRatio=1: 设置年轻代(Young Generation)与老年代(Old Generation)的比例。这里设置为1,意味着年轻代和老年代的大小将相等。
-XX:SurvivorRatio=30: 设置年轻代中Eden区与一个Survivor区的大小比例。这里的设置为30,意味着Eden区将是Survivor区大小的30倍。
-XX:+UseParallelGC: 启用并行垃圾收集器。这种收集器在多核处理器上表现良好。
-XX:+UseParallelOldGC: 对老年代使用并行垃圾收集。
1
miniliuke 317 天前
堆 dump 看看
|
2
twofox 317 天前
dump 下来去分析内存
|
3
zed1018 317 天前 7
找算命先生测算一下
|
4
cxshun 317 天前
超出堆内存大小,应该就是堆外内存的问题了。用上面兄弟的 jmap dump 出来看看。但要注意会触发 FGC 。
|
5
me1onsoda 317 天前
xmx 限制了,那就是堆外泄露了呗
|
6
visper 317 天前
没挂能跑就行,管它呢。配置的 Xmx 只是堆内存大小,jvm 自己使用的内存,文件句柄啊,线程本身内存啊的都不在这里。
|
7
dengji85 317 天前
蹲一下解决方案,我有个开发的应用也是这样,时间越长内存越高,慢慢稳定在约束的最大内存,永远不会自动是释放内存,推测是写代码习惯问题,导致对象得不到释放
|
8
cnzjl 317 天前
arthas 在线看一下内存占用情况
|
9
halov 317 天前
我们公司现在使用 消息中间件 nats 也有这种问题 😅
|
10
cheng6563 317 天前 4
管他呢,搞个定时重启完事
|
11
chenPiMeiHaoChi 317 天前
查查有没有静态的集合,一般这种稳定增长的就是静态 map 或者 list 越来越大。不过我也建议写个定时重启算了。
|
12
timeisweapon 317 天前
这个得看你的项目有多大,小项目内存持续增大,大概率是代码问题,没有及时释放内存,需要看 dump
|
13
wxw752 317 天前
|
14
salmon5 317 天前
每天重启下就好了,java 就这样的
|
16
kaedea 317 天前 via Android
hprof
|
17
salmon5 317 天前
启动参数加上-XX:NativeMemoryTracking=summary ,然后 jcmd pid VM.native_memory 分析下
|
18
Shinu 317 天前
巧了, 我也遇到过这个问题, 不过增长的慢, 离职了都没想着去解决这个问题. 蹲下排查方法
|
19
starlin 317 天前 1
别理解错了,xmx 只限制了堆的最大值,还有非堆,线程栈,代码缓存,jvm 本身运行所需要的内存等,如有必要可以配置 NMT 进行分析,但是注意有性能损耗
|
20
676529483 317 天前
mark 等个大佬,公司项目也是,看 arms 堆内存+非堆不到占用的内存,怀疑堆外又没办法排查,最后只能重启
|
23
Scarb 317 天前 1
2.6G 内存,超过堆内存的上限 2G ,堆外内存泄漏了。先 smaps 看下堆内存占用多少,算出堆外内存占用。堆外内存如果没特殊配置,最大也只能占 2G ,可能很快就 OOM 了。
堆外内存很难定位泄漏点,给一个参考: https://tech.meituan.com/2018/10/18/netty-direct-memory-screening.html 实在不行只能把内存用 gdb dump 出来,然后强行查看里面的内容,推测可能的泄漏原因。 |
27
LowBi 317 天前 via iPhone
哈哈哈 我的是内存正常 但是十几天后占用内存变小 随后宕机 问题是内存泄露 高并发的读取数据库导致有些线程超时未能关掉 好烦 不多线程的话 方法执行完需要三分多钟 现在还没想到长期运行的办法
|
28
JYii 317 天前
@salmon5 #24 想问下排查内存泄漏这种问题对于 Java 开发是什么层次。
曾经解决过公司一个服务的内存泄漏,堆外问题确实很难定位到,从简单的 NativeMemoryTracking ,pmap 查看内存分配,监控栈函数调用,jeprof 对比堆内分配,最终定位到框架的问题,确实在对应版本 issue 中找到,升级后解决。 |
29
trcnkq 317 天前 6
* JVM 不是它需要多少内存,才占用多少内存;而是在 -Xmx 允许的前提下,只要你系统还有空余,它就会大方地申请占用;而且即使之前申请了,后续不需要这么多了,它也不会及时退还给 OS ;
* 你可以通过 jhsdb jmap --heap --pid ${jvm_pid} 来确认你的应用当前实际只 需要/占用 多少内存; * 如果你希望让 JVM 需要多少内存,才占用多少内存,多占用了就及时释放,可以通过调整 -XX:MinHeapFreeRatio -XX:MaxHeapFreeRatio 来实现。 |
30
trcnkq 317 天前
当然上面所说,前提是你的应用没有内存泄漏。
|
32
salmon5 317 天前
@JYii #28 P7 P8 我说的可能有点夸张了,但实际中大部分 Java 开发不会(不想浪费时间或者没有能力)这么排查,大部分就是扩容服务器、容器内存+定期重启解决
|
34
nothingistrue 317 天前
-Xms -Xmx 限制的是堆内存,JVM 不止有堆内存,JVM 内存大于 Xmx 是正常情况。
堆内存只是用来存储对象的成员变量的,对象的方法运行期间使用的基本类型变量(包括数组),要占用栈内存,这些是随运行动态申请和释放的。如果是服务器负荷期间内存升高,无需理会,负荷降了就会自己下去。 Java 类定义,类的成员方法的函数定义,类的静态变量,也是要占用内存的。这些内存通常是一旦加载就不会释放(具体取决于类加载器),这种情况会导致 JVM 启动一段时间后内存就略微增长。但这种情况也请无需理会,首先这是 JVM 的职责,其次就算它真是屎山你也不该去碰。 |
36
ChaYedan666 317 天前
堆外内存设置 JVM 管不着,项目迭代快就懒得管了(基本一两周就会上线一次),没崩就行
|
37
Arumoh 317 天前
换 openj9
|
38
SilenceLL 317 天前
看内存快照,里面看下 dominator_tree 最大对象是不是有啥问题
|
39
joyhub2140 317 天前
很好奇,都说堆外内存泄露的,难不成都是用 jni 开发 Java 项目嘛?
|
40
falsemask 317 天前
你的 jdk 版本号多少,我遇到过 java8 低版本 bug ,会导致堆外内存一直涨
|
41
diagnostics 317 天前
@joyhub2140 哥们,线程不占内存?
|
42
diagnostics 317 天前
-XMX 定好了,那大概率是堆外,不确定你这是 RSS ,还是 COMMITED 的 2.6G ,建议开 NMT 然后看一下
假如没有 native memory leak ,大概率是有线程泄漏了,导致内存泄漏了 |
43
diagnostics 317 天前 1
@JYii #28 层次是和别人对比出来的
例如你觉得自己底层厉害,想找基础框架的,那么和基础框架组的人对比,你的能力是什么范围,懂底层的多少东西,如果只会内存泄漏这一个,其他的不熟悉,那你定级就比较低。假如你 JVM 精通、线程模型精通、数据结构精通,网络精通,那又是另一个层面。 排查问题是一种能力,根据知识来设计软件架构又是一种能力 |
44
twofox 317 天前
@nothingistrue 我觉得你说的有道理。xmx 确实只限制堆内存。内存的问题就应该交给 JVM 处理。再怎么调优都比不过新的 jdk 带的垃圾收集器
优先排查代码问题,再考虑 JVM 调优 知乎的这篇文章可能对 OP 有帮助 https://zhuanlan.zhihu.com/p/432258798 |
45
JYii 317 天前
@diagnostics #43 OK 了解,多谢回复,学习成长路漫漫。(后面几个精通给我干懵了
|
46
chenfang 317 天前
2.6G 如果不出现内存报警,要么你的 2.6G 不准,要么有堆外内存(比如 netty 可以创建堆外内存),
同时也建议看看 gc 次数频繁不频繁 你也说了是一周之后 2.6G,但是这个并没有什么参考,你需要看这一周的内存变化,来分析情况 |
47
ASpiral 317 天前
起个定时任务强制 full gc ,都不用重启了/dog
|
48
nananqujava 317 天前
先升级 jdk 版本看看
|
49
iamhucong 317 天前
多对比几次 smaps 看堆外内存有没有持续上涨
|
50
Dongxiaohao 317 天前 via Android
@JYii 笑死了,当初我们开发的一个项目,根据模板渲染 Excel 表格,里面的图片很多(五六十张吧),最后转成 PDF 导出,导致这个接口但凡调用一次就 fullgc 几次,最后找不出问题就写了个定时任务隔一段时间重启服务😂
|
51
JYii 317 天前
@Dongxiaohao #50 fullGC 频繁,代码不太烂的话,加内存就好了。(省心省力
|
52
ZSeptember 317 天前
没有监控吗。
一般就是 dump 下来,使用 memory analyzer 分析一下 |
53
qiubinren 317 天前
项目里面有自己用 netty 直接内存池进行分配和释放么?用了加-Dio.netty.leakDetection.level=PARANOID 参数,检测下 netty 侧内存有没有泄漏,隔段时间在日志里搜 LEAK 就行了。
如果没有直接玩 netty 内存池,加 @salmon5 #17 提到的参数分析 NMT ,多收集几次,看下具体哪个区域在不断增大,收集更多的信息,另外最好把 jdk 版本贴出来,遇到过 openjdk 连续几个小版本反射导致内存泄漏,如果 jdk 小版本版本号太小,最好直接升级到小版本较高的版本 |
54
zx9481 OP 感谢 项目中没有用到 netty 我去加启动参数看看
|
55
qiubinren 317 天前
@zx9481 我上面没有细看你的命令,直接断定你的程序存在直接内存泄漏,这其实不太对。又看了眼你的命令,堆内存给了 2G ,一周跑下来 2.6G ,其实感觉很可能是正常的,因为命令里只限制堆内存,其他区域并没有任何限制,至少把 MaxDirectMemorySize 、ReservedCodeCacheSize 、MaxMetaspaceSize 这几个参数填了。如果还限制不住,可以考虑增加-Djdk.nio.maxCachedBufferSize 、-DMALLOC_ARENA_MAX 、-DMALLOC_MMAP_THRESHOLD_这些参数的配置,并且给比较小的值,如果还是不断增长,再考虑内存泄漏。
|
56
duron600 317 天前
而内存的总量保持不变。
|
57
susuper 317 天前
是如何确认内存不断增大呢,通过 top 吗
|
58
hancai 317 天前
老生常谈的问题了, k8s limit pod 的内存限制, 触发 OOM 就释放了,就是这么粗暴。
|
59
Ericcccccccc 317 天前
先 dump 简单分析下再看其它问题.
|
61
dyv9 316 天前 via Android
7* 24 后台线程不能在 循环里声明变量,否则内存会爆。
|
62
alex8 316 天前
感觉没问题,还有堆外内存呢,Metadata ,Codecache 等。使用中 jit 会把热点字节码编译成机器码放到 codecache 中,这个增长不会一直持续。
|
63
Znemo 316 天前
@wxw752 Netty 的 buffer 是基于计数器来决定是否释放的,每个 handler 有义务对不再使用的 buffer 做减少引用的操作,继承了 SimpleChannelInboundHandler 的话会自动执行一次减少引用的行为,在 handler 链的末端会再次做一次减少引用的动作,如果 handler 链执行完,buffer 的引用计数为 0 就会被释放,所以有些业务需要会通过 ReferenceCountUtil.retain() 增加引用计数,避免 buffer 被释放,不小心会导致 buffer 泄露,实际上在 netty 中已经内置了泄露分析的工具,可以考虑在测试环境添加 io.netty.leakDetectionLevel 参数来分析是否存在 buffer 泄露。
|
64
zx9481 OP 目前解决了哈 感谢大家😘
|