V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zx9481
V2EX  ›  Java

Java 应用内存不断增长

  •  2
     
  •   zx9481 · 106 天前 · 6557 次点击
    这是一个创建于 106 天前的主题,其中的信息可能已经有所发展或是发生改变。

    启动参数如下

    java -server -Xms2048m -Xmx2048m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/xxx/heapError -jar xxx.jar --spring.profiles.active=prod --server.port=9551
    

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

    第 1 条附言  ·  106 天前

    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)
    
    第 2 条附言  ·  101 天前

    在听取了 @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: 对老年代使用并行垃圾收集。

    64 条回复    2024-01-22 09:39:08 +08:00
    miniliuke
        1
    miniliuke  
       106 天前
    堆 dump 看看
    twofox
        2
    twofox  
       106 天前
    dump 下来去分析内存
    zed1018
        3
    zed1018  
       106 天前   ❤️ 7
    找算命先生测算一下
    cxshun
        4
    cxshun  
       106 天前
    超出堆内存大小,应该就是堆外内存的问题了。用上面兄弟的 jmap dump 出来看看。但要注意会触发 FGC 。
    me1onsoda
        5
    me1onsoda  
       106 天前
    xmx 限制了,那就是堆外泄露了呗
    visper
        6
    visper  
       106 天前
    没挂能跑就行,管它呢。配置的 Xmx 只是堆内存大小,jvm 自己使用的内存,文件句柄啊,线程本身内存啊的都不在这里。
    dengji85
        7
    dengji85  
       106 天前
    蹲一下解决方案,我有个开发的应用也是这样,时间越长内存越高,慢慢稳定在约束的最大内存,永远不会自动是释放内存,推测是写代码习惯问题,导致对象得不到释放
    cnzjl
        8
    cnzjl  
       106 天前
    arthas 在线看一下内存占用情况
    halov
        9
    halov  
       106 天前
    我们公司现在使用 消息中间件 nats 也有这种问题 😅
    cheng6563
        10
    cheng6563  
       106 天前   ❤️ 4
    管他呢,搞个定时重启完事
    chenPiMeiHaoChi
        11
    chenPiMeiHaoChi  
       106 天前
    查查有没有静态的集合,一般这种稳定增长的就是静态 map 或者 list 越来越大。不过我也建议写个定时重启算了。
    timeisweapon
        12
    timeisweapon  
       106 天前
    这个得看你的项目有多大,小项目内存持续增大,大概率是代码问题,没有及时释放内存,需要看 dump
    wxw752
        13
    wxw752  
       106 天前
    巧了,我写的其中一个 netty 也是这样,后来实在找不到问题了,每两个月重启一下
    salmon5
        14
    salmon5  
       106 天前
    每天重启下就好了,java 就这样的
    jorneyr
        15
    jorneyr  
       106 天前
    @wxw752 巧了,我写的其中一个 netty 也是这样,后来实在找不到问题了,每两个月重启一下

    Netty 大量使用了堆外内存。
    kaedea
        16
    kaedea  
       106 天前 via Android
    hprof
    salmon5
        17
    salmon5  
       106 天前
    启动参数加上-XX:NativeMemoryTracking=summary ,然后 jcmd pid VM.native_memory 分析下
    Shinu
        18
    Shinu  
       106 天前
    巧了, 我也遇到过这个问题, 不过增长的慢, 离职了都没想着去解决这个问题. 蹲下排查方法
    starlin
        19
    starlin  
       106 天前   ❤️ 1
    别理解错了,xmx 只限制了堆的最大值,还有非堆,线程栈,代码缓存,jvm 本身运行所需要的内存等,如有必要可以配置 NMT 进行分析,但是注意有性能损耗
    676529483
        20
    676529483  
       106 天前
    mark 等个大佬,公司项目也是,看 arms 堆内存+非堆不到占用的内存,怀疑堆外又没办法排查,最后只能重启
    me1onsoda
        21
    me1onsoda  
       106 天前
    @wxw752 @jorneyr netty 这种应用内存就是跟着连接数涨的,吃的是堆外内存,一条连接一个读写缓冲区。gc 管不着
    burymme11
        22
    burymme11  
       106 天前
    @wxw752
    巧了,我们把网关升级到 spring gateway 之后,也出现这问题,就是 netty 申请的堆外内存导致的。
    Scarb
        23
    Scarb  
       106 天前   ❤️ 1
    2.6G 内存,超过堆内存的上限 2G ,堆外内存泄漏了。先 smaps 看下堆内存占用多少,算出堆外内存占用。堆外内存如果没特殊配置,最大也只能占 2G ,可能很快就 OOM 了。

    堆外内存很难定位泄漏点,给一个参考:
    https://tech.meituan.com/2018/10/18/netty-direct-memory-screening.html

    实在不行只能把内存用 gdb dump 出来,然后强行查看里面的内容,推测可能的泄漏原因。
    salmon5
        24
    salmon5  
       106 天前
    @Scarb #23 你说的这个,又是 smaps ,又是 gdb ,大部分的 java 开发不懂
    salmon5
        25
    salmon5  
       106 天前
    @salmon5 不是人均阿里 P7 P8 水平,大部分还是 CRUD
    aLazarus
        26
    aLazarus  
       106 天前
    @salmon5 #24 不过还是可以学到知识
    LowBi
        27
    LowBi  
       106 天前 via iPhone
    哈哈哈 我的是内存正常 但是十几天后占用内存变小 随后宕机 问题是内存泄露 高并发的读取数据库导致有些线程超时未能关掉 好烦 不多线程的话 方法执行完需要三分多钟 现在还没想到长期运行的办法
    JYii
        28
    JYii  
       106 天前
    @salmon5 #24 想问下排查内存泄漏这种问题对于 Java 开发是什么层次。
    曾经解决过公司一个服务的内存泄漏,堆外问题确实很难定位到,从简单的 NativeMemoryTracking ,pmap 查看内存分配,监控栈函数调用,jeprof 对比堆内分配,最终定位到框架的问题,确实在对应版本 issue 中找到,升级后解决。
    trcnkq
        29
    trcnkq  
       106 天前   ❤️ 5
    * JVM 不是它需要多少内存,才占用多少内存;而是在 -Xmx 允许的前提下,只要你系统还有空余,它就会大方地申请占用;而且即使之前申请了,后续不需要这么多了,它也不会及时退还给 OS ;
    * 你可以通过 jhsdb jmap --heap --pid ${jvm_pid} 来确认你的应用当前实际只 需要/占用 多少内存;
    * 如果你希望让 JVM 需要多少内存,才占用多少内存,多占用了就及时释放,可以通过调整 -XX:MinHeapFreeRatio -XX:MaxHeapFreeRatio 来实现。
    trcnkq
        30
    trcnkq  
       106 天前
    当然上面所说,前提是你的应用没有内存泄漏。
    JYii
        31
    JYii  
       106 天前
    @salmon5 #24 对于一些非重要服务,楼上的低频时段重启确实简单暴力,排查内存泄漏问题起码两周起步
    salmon5
        32
    salmon5  
       106 天前
    @JYii #28 P7 P8 我说的可能有点夸张了,但实际中大部分 Java 开发不会(不想浪费时间或者没有能力)这么排查,大部分就是扩容服务器、容器内存+定期重启解决
    storyxc
        33
    storyxc  
       106 天前
    @Scarb 学到了
    nothingistrue
        34
    nothingistrue  
       106 天前
    -Xms -Xmx 限制的是堆内存,JVM 不止有堆内存,JVM 内存大于 Xmx 是正常情况。

    堆内存只是用来存储对象的成员变量的,对象的方法运行期间使用的基本类型变量(包括数组),要占用栈内存,这些是随运行动态申请和释放的。如果是服务器负荷期间内存升高,无需理会,负荷降了就会自己下去。

    Java 类定义,类的成员方法的函数定义,类的静态变量,也是要占用内存的。这些内存通常是一旦加载就不会释放(具体取决于类加载器),这种情况会导致 JVM 启动一段时间后内存就略微增长。但这种情况也请无需理会,首先这是 JVM 的职责,其次就算它真是屎山你也不该去碰。
    JYii
        35
    JYii  
       106 天前
    @salmon5 #32 我没有大厂经验,想了解下现在的行情。年后要重新找工作(允悲
    ChaYedan666
        36
    ChaYedan666  
       106 天前
    堆外内存设置 JVM 管不着,项目迭代快就懒得管了(基本一两周就会上线一次),没崩就行
    Arumoh
        37
    Arumoh  
       106 天前
    换 openj9
    SilenceLL
        38
    SilenceLL  
       106 天前
    看内存快照,里面看下 dominator_tree 最大对象是不是有啥问题
    joyhub2140
        39
    joyhub2140  
       106 天前
    很好奇,都说堆外内存泄露的,难不成都是用 jni 开发 Java 项目嘛?
    falsemask
        40
    falsemask  
       106 天前
    你的 jdk 版本号多少,我遇到过 java8 低版本 bug ,会导致堆外内存一直涨
    diagnostics
        41
    diagnostics  
       106 天前
    @joyhub2140 哥们,线程不占内存?
    diagnostics
        42
    diagnostics  
       106 天前
    -XMX 定好了,那大概率是堆外,不确定你这是 RSS ,还是 COMMITED 的 2.6G ,建议开 NMT 然后看一下

    假如没有 native memory leak ,大概率是有线程泄漏了,导致内存泄漏了
    diagnostics
        43
    diagnostics  
       106 天前   ❤️ 1
    @JYii #28 层次是和别人对比出来的

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

    排查问题是一种能力,根据知识来设计软件架构又是一种能力
    twofox
        44
    twofox  
       106 天前
    @nothingistrue 我觉得你说的有道理。xmx 确实只限制堆内存。内存的问题就应该交给 JVM 处理。再怎么调优都比不过新的 jdk 带的垃圾收集器

    优先排查代码问题,再考虑 JVM 调优

    知乎的这篇文章可能对 OP 有帮助
    https://zhuanlan.zhihu.com/p/432258798
    JYii
        45
    JYii  
       106 天前
    @diagnostics #43 OK 了解,多谢回复,学习成长路漫漫。(后面几个精通给我干懵了
    chenfang
        46
    chenfang  
       106 天前
    2.6G 如果不出现内存报警,要么你的 2.6G 不准,要么有堆外内存(比如 netty 可以创建堆外内存),

    同时也建议看看 gc 次数频繁不频繁

    你也说了是一周之后 2.6G,但是这个并没有什么参考,你需要看这一周的内存变化,来分析情况
    ASpiral
        47
    ASpiral  
       106 天前
    起个定时任务强制 full gc ,都不用重启了/dog
    nananqujava
        48
    nananqujava  
       106 天前
    先升级 jdk 版本看看
    iamhucong
        49
    iamhucong  
       106 天前
    多对比几次 smaps 看堆外内存有没有持续上涨
    Dongxiaohao
        50
    Dongxiaohao  
       106 天前 via Android
    @JYii 笑死了,当初我们开发的一个项目,根据模板渲染 Excel 表格,里面的图片很多(五六十张吧),最后转成 PDF 导出,导致这个接口但凡调用一次就 fullgc 几次,最后找不出问题就写了个定时任务隔一段时间重启服务😂
    JYii
        51
    JYii  
       106 天前
    @Dongxiaohao #50 fullGC 频繁,代码不太烂的话,加内存就好了。(省心省力
    ZSeptember
        52
    ZSeptember  
       106 天前
    没有监控吗。
    一般就是 dump 下来,使用 memory analyzer 分析一下
    qiubinren
        53
    qiubinren  
       106 天前
    项目里面有自己用 netty 直接内存池进行分配和释放么?用了加-Dio.netty.leakDetection.level=PARANOID 参数,检测下 netty 侧内存有没有泄漏,隔段时间在日志里搜 LEAK 就行了。
    如果没有直接玩 netty 内存池,加 @salmon5 #17 提到的参数分析 NMT ,多收集几次,看下具体哪个区域在不断增大,收集更多的信息,另外最好把 jdk 版本贴出来,遇到过 openjdk 连续几个小版本反射导致内存泄漏,如果 jdk 小版本版本号太小,最好直接升级到小版本较高的版本
    zx9481
        54
    zx9481  
    OP
       106 天前
    感谢 项目中没有用到 netty 我去加启动参数看看
    qiubinren
        55
    qiubinren  
       106 天前
    @zx9481 我上面没有细看你的命令,直接断定你的程序存在直接内存泄漏,这其实不太对。又看了眼你的命令,堆内存给了 2G ,一周跑下来 2.6G ,其实感觉很可能是正常的,因为命令里只限制堆内存,其他区域并没有任何限制,至少把 MaxDirectMemorySize 、ReservedCodeCacheSize 、MaxMetaspaceSize 这几个参数填了。如果还限制不住,可以考虑增加-Djdk.nio.maxCachedBufferSize 、-DMALLOC_ARENA_MAX 、-DMALLOC_MMAP_THRESHOLD_这些参数的配置,并且给比较小的值,如果还是不断增长,再考虑内存泄漏。
    duron600
        56
    duron600  
       106 天前
    而内存的总量保持不变。
    susuper
        57
    susuper  
       106 天前
    是如何确认内存不断增大呢,通过 top 吗
    hancai
        58
    hancai  
       106 天前
    老生常谈的问题了, k8s limit pod 的内存限制, 触发 OOM 就释放了,就是这么粗暴。
    Ericcccccccc
        59
    Ericcccccccc  
       106 天前
    先 dump 简单分析下再看其它问题.
    PDX
        60
    PDX  
       105 天前
    @wxw752 有 buffer 没释放掉
    dyv9
        61
    dyv9  
       105 天前 via Android
    7* 24 后台线程不能在 循环里声明变量,否则内存会爆。
    alex8
        62
    alex8  
       105 天前
    感觉没问题,还有堆外内存呢,Metadata ,Codecache 等。使用中 jit 会把热点字节码编译成机器码放到 codecache 中,这个增长不会一直持续。
    Znemo
        63
    Znemo  
       105 天前
    @wxw752 Netty 的 buffer 是基于计数器来决定是否释放的,每个 handler 有义务对不再使用的 buffer 做减少引用的操作,继承了 SimpleChannelInboundHandler 的话会自动执行一次减少引用的行为,在 handler 链的末端会再次做一次减少引用的动作,如果 handler 链执行完,buffer 的引用计数为 0 就会被释放,所以有些业务需要会通过 ReferenceCountUtil.retain() 增加引用计数,避免 buffer 被释放,不小心会导致 buffer 泄露,实际上在 netty 中已经内置了泄露分析的工具,可以考虑在测试环境添加 io.netty.leakDetectionLevel 参数来分析是否存在 buffer 泄露。
    zx9481
        64
    zx9481  
    OP
       96 天前
    目前解决了哈 感谢大家😘
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   890 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 38ms · UTC 21:23 · PVG 05:23 · LAX 14:23 · JFK 17:23
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.