Payara服务器上Java应用无响应问题求助(附JVM配置)
排查Payara Java应用频繁无响应问题的实用思路
先理清楚咱们的核心场景:2GB内存虚拟机跑Payara,Java应用堆固定1GB,用G1GC且目标停顿200ms,主要做队列拉取和HTTP请求响应,现在频繁无响应,重启能好,但堆转储还不完整。下面一步步来排查:
一、先搞定完整的堆转储——这是排查的基础
截断的堆转储基本没法分析,得先拿到有效的文件:
- 检查磁盘空间:堆转储会写到磁盘,至少要留够1.5倍堆内存的空间(G1GC的转储可能比实际堆占用略大)。先看看应用所在磁盘的可用空间,如果满了,转储肯定会被截断。
- 调整转储方式:如果用
jmap,试试jmap -dump:live,format=b,file=heap.hprof <pid>,只转储存活对象,文件会小一些;或者在Payara控制台配置OOM自动转储,加JVM参数-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/safe/dir,确保触发转储的条件正确。 - 僵死JVM的转储方法:如果应用无响应时JVM已经僵死,
jmap连不上的话,Linux下可以用gcore <pid>生成核心转储,再用jhsdb jmap --heap --exe <java-path> --core <core-file>转成堆转储格式分析。
二、从JVM参数和GC行为找线索
你用的G1GC参数有几个点值得深挖:
- 开启详细GC日志:加参数
-Xlog:gc*,gc+heap=trace,gc+age=trace:file=/var/log/payara/gc.log:time,uptime,level,tags,重点看这几个指标:- 是否频繁触发Full GC?G1GC频繁Full GC通常是老年代空间不足,或者并发标记的速度赶不上对象分配的速度。
- 实际GC停顿时间是不是远超200ms?如果
MaxGCPauseMillis设得太严,G1可能会频繁调整堆区域大小,反而导致更多停顿甚至僵死。 - 新生代是不是太小?
NewRatio=2让新生代只占堆的1/3(约333MB),如果应用有大量临时对象(比如HTTP请求的参数、队列拉取的数据包),新生代很快填满,会频繁Minor GC,甚至提前把对象晋升到老年代,加速老年代耗尽。
- 内存泄漏排查:就算没有OOM日志,也可能是内存泄漏导致老年代持续增长,最终引发GC风暴(JVM一直在GC,没精力处理业务请求)。拿到完整堆转储后,用MAT(Memory Analyzer Tool)或者VisualVM分析:
- 找出占内存最大的对象类型,看看是不是队列拉取的数据没及时释放,或者缓存没设过期时间。
- 查对象的引用链,有没有静态集合、单例持有大量对象导致无法回收。
三、排查线程阻塞或资源耗尽问题
应用无响应不一定是内存的锅,线程阻塞也很常见:
- 抓线程转储:应用无响应时立刻用
jstack <pid>或者Payara控制台的线程dump功能抓线程栈,重点看:- 是不是大量线程阻塞在同一个锁上?比如队列拉取线程和HTTP请求线程抢同一个资源,导致死锁或长时间等待。
- 有没有线程卡在IO操作上?比如拉队列数据时,远程服务器响应慢,又没设超时,线程挂起后占着线程池,最后线程池耗尽。
- 检查Payara的线程池配置:HTTP请求线程池、EJB线程池的大小够不够?如果请求量超过上限,新请求排队,就会显得应用无响应。
- 队列拉取逻辑检查:你的应用要从远程服务器拉队列数据,得确认:
- 拉取操作有没有超时设置?比如用JMS的话,是不是用
receive(timeout)而不是无限等待? - 重试逻辑会不会导致线程堆积?如果拉取失败后无限制重试,会占满线程池。
- 拉取操作有没有超时设置?比如用JMS的话,是不是用
四、排查虚拟机的资源瓶颈
虚拟机总内存2GB,堆占1GB,剩下的1GB要分给JVM非堆内存(元空间、直接内存、线程栈、JVM本身开销),可能存在资源不足:
- 直接内存限制:如果应用用了NIO(比如HTTP请求、队列拉取用了ByteBuffer),直接内存不在堆里,但会占系统内存。可以加参数
-XX:MaxDirectMemorySize=256m限制大小,避免耗尽系统内存导致OOM或swap(swap会让JVM性能暴跌)。 - 检查系统swap:如果虚拟机内存不够,系统会用swap,JVM对swap特别敏感,一旦开始swap,GC停顿时间会急剧增加,甚至僵死。用
free -m或vmstat看看有没有swap使用,如果有,要么加虚拟机内存,要么把堆调到768MB,给非堆留更多空间。
内容的提问来源于stack exchange,提问作者sheikhisham
相关产品推荐
相关产品推荐

