java服务,cpu高,内存高,telnet不通排查及分析

本文记录了一个Java服务出现CPU使用率高、内存占用大及服务端口响应异常的问题排查过程。通过对异常线程的分析,发现GC线程长时间繁忙,进一步通过JVM参数调整和优化队列长度解决了问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一个java服务的异常排查及分析过程,以备将来参考。

 

java服务的异常主要表现为3点:

1.cpu使用率高;

2.内存占用较大;

3.本机telnet访问服务被拒绝

 

具体情况:

1.cpu高。启动时会打到800%以上,访问量不大的时段,top看到使用率在100-400%之间,基本稳定在100%-200%左右。

 

2.内存高。启动后java服务占用的内存不断增大,服务器12G物理内存,增大到12*26%=3G左右时,不再增大,但服务器物理内存也已接近用尽。

 

3.telnet服务端口没响应。应用刚启动时,telnet 可通,但过10-20分钟左右,服务开始不响应本机测试的telnet 请求。

 

分析步骤:

1.定位占用cpu高的java线程。

top -H -p 21382 -d 1 -n3 > test_top_thread_20131221_12.log

 

通过top命令将java进程中占用cpu高的线程进行排序,并输出到文件。通过查看可以很容易的定位线程21397占用了较高的cpu和内存,分别为100.6%和物理内存的26.7%,相当异常。

 

2.查看该线程的执行时间。

ps -mp 21382 -o THREAD,tid,time > test_cpu_time_20131221_12.out

 

通过命令查询线程的执行时间,并将输出保存到文件。可以看到线程21397已经运行了13个小时左右,从服务重启到排查的时候刚好13个小时左右,说明该线程一直在繁忙,于是通过pstack看看该线程在忙啥呢。

 

3.通过pstack查看进程的栈信息。

pstack 21382 > test_pstack_20131221_12.out

 

从pstack的输出找到对应线程的信息,如下:

 

Xml代码  收藏代码
  1. Thread 150 (Thread 0x7f3aa7fff700 (LWP 21397)):  
  2. #0  0x00007f3ae62fcdc4 in instanceKlass::oop_follow_contents(oopDesc*) () from /usr/java/jdk1.6.0_31/jre/lib/amd64/server/libj  
  3. vm.so  
  4. #1  0x00007f3ae64e83eb in MarkSweep::follow_stack() () from /usr/java/jdk1.6.0_31/jre/lib/amd64/server/libjvm.so  
  5. #2  0x00007f3ae65a1f14 in PSMarkSweep::mark_sweep_phase1(bool) () from /usr/java/jdk1.6.0_31/jre/lib/amd64/server/libjvm.so  
  6. #3  0x00007f3ae65a0dc1 in PSMarkSweep::invoke_no_policy(bool) () from /usr/java/jdk1.6.0_31/jre/lib/amd64/server/libjvm.so  
  7. #4  0x00007f3ae65ad927 in PSScavenge::invoke() () from /usr/java/jdk1.6.0_31/jre/lib/amd64/server/libjvm.so  
  8. #5  0x00007f3ae656f90e in ParallelScavengeHeap::failed_mem_allocate(unsigned long, bool) () from /usr/java/jdk1.6.0_31/jre/lib  
  9. /amd64/server/libjvm.so  
  10. #6  0x00007f3ae66ac3d8 in VM_ParallelGCFailedAllocation::doit() () from /usr/java/jdk1.6.0_31/jre/lib/amd64/server/libjvm.so  
  11. #7  0x00007f3ae66b97ea in VM_Operation::evaluate() () from /usr/java/jdk1.6.0_31/jre/lib/amd64/server/libjvm.so  
  12. #8  0x00007f3ae66b8db2 in VMThread::evaluate_operation(VM_Operation*) () from /usr/java/jdk1.6.0_31/jre/lib/amd64/server/libjv  
  13. m.so  
  14. #9  0x00007f3ae66b9028 in VMThread::loop() () from /usr/java/jdk1.6.0_31/jre/lib/amd64/server/libjvm.so  
  15. #10 0x00007f3ae66b8b2e in VMThread::run() () from /usr/java/jdk1.6.0_31/jre/lib/amd64/server/libjvm.so  
  16. #11 0x00007f3ae655cbdf in java_start(Thread*) () from /usr/java/jdk1.6.0_31/jre/lib/amd64/server/libjvm.so  
  17. #12 0x0000003a61c077f1 in start_thread () from /lib64/libpthread.so.0  
  18. #13 0x0000003a618e570d in clone () from /lib64/libc.so.6  

 

可看到该线程一直在忙着VM_ParallelGCFailedAllocation::doit(),和GC的内存分配和回收有关,往下看,就更清楚了,

 

C代码  收藏代码
  1. ParallelScavengeHeap::failed_mem_allocate(unsigned longbool)  

是在分配heap空间,说明java应用的heap空间可能不足,导致GC线程一直在忙活分配新的空间。 继续看

 

MarkSweep::follow_stack()在干吗,查下源代码,如下:

 

Cpp代码  收藏代码
  1. // Flush marking stack.  
  2.   follow_stack();  
  3.   
  4.   // Process reference objects found during marking  
  5.   {  
  6.     ref_processor()->setup_policy(clear_all_softrefs);  
  7.     ref_processor()->process_discovered_references(  
  8.       is_alive_closure(), mark_and_push_closure(), follow_stack_closure(), NULL);  
  9.   }  

 该方法在清除已经标记的java对象,说明对象太多,才会导致GC一直在想办法清理heap,同时也说明java heap空间不足。联想到该服务对对象的创建和服务,初步分析可能是服务队列里对象太多导致出现该问题。

 

 

4.查看jvm参数配置。

jinfo -flag Xmx 21382

 

 

Xml代码  收藏代码
  1. Heap  
  2.  PSYoungGen      total 984448K, used 546524K [0x00000007c2200000, 0x00000007ffe30000, 0x0000000800000000)  
  3.   eden space 957760K, 57% used [0x00000007c2200000,0x00000007e37b7088,0x00000007fc950000)  
  4.   from space 26688K, 0% used [0x00000007fe420000,0x00000007fe420000,0x00000007ffe30000)  
  5.   to   space 27072K, 0% used [0x00000007fc950000,0x00000007fc950000,0x00000007fe3c0000)  
  6.  PSOldGen        total 204224K, used 112010K [0x0000000746600000, 0x0000000752d70000, 0x00000007c2200000)  
  7.   object space 204224K, 54% used [0x0000000746600000,0x000000074d362800,0x0000000752d70000)  
  8.  PSPermGen       total 44928K, used 22198K [0x0000000741400000, 0x0000000743fe0000, 0x0000000746600000)  
  9.   object space 44928K, 49% used [0x0000000741400000,0x00000007429adbc0,0x0000000743fe0000)  

 

 

 

Java代码  收藏代码
  1. Heap  
  2. PSYoungGen      total 881024K, used 451396K [0x00000007c22000000x00000008000000000x0000000800000000)  
  3.  eden space 788480K, 45% used [0x00000007c2200000,0x00000007d8079b80,0x00000007f2400000)  
  4.  from space 92544K, 99% used [0x00000007f2400000,0x00000007f7e57580,0x00000007f7e60000)  
  5.  to   space 119488K, 0% used [0x00000007f8b50000,0x00000007f8b50000,0x0000000800000000)  
  6. PSOldGen        total 1561792K, used 1226956K [0x00000007466000000x00000007a5b300000x00000007c2200000)  
  7.  object space 1561792K, 78% used [0x0000000746600000,0x00000007914330e0,0x00000007a5b30000)  
  8. PSPermGen       total 37888K, used 22595K [0x00000007414000000x00000007439000000x0000000746600000)  
  9.  object space 37888K, 59% used [0x0000000741400000,0x0000000742a10cb8,0x0000000743900000)  

 

以上两条输出是应用刚启动和启动20分钟后的heap使用对比, PSOldGen从 204224K(204M)增大到了

1561792K(1.5G左右),说明当前java应用对老生代的需求较大,才导致老生代的空间暴涨;另外,

PSYoungGen的from也从26688K(26M)增大到了92544K(95M),说明有很多对象等待被清理。继续看输出:

 

 

 

Java代码  收藏代码
  1. "Thread-23" prio=10 tid=0x00007f19f022e000 nid=0x19ac waiting for monitor entry [0x00007f19a0958000]  
  2.    java.lang.Thread.State: BLOCKED (on object monitor)  
  3.         at org.apache.log4j.Category.callAppenders(Category.java:204)  
  4.         - waiting to lock <0x0000000749823ca8> (a org.apache.log4j.spi.RootLogger)  
  5.         at org.apache.log4j.Category.forcedLog(Category.java:391)  
  6.         at org.apache.log4j.Category.log(Category.java:856)  
  7.         at org.slf4j.impl.Log4jLoggerAdapter.error(Log4jLoggerAdapter.java:498)  
  8.         at com.chinahh.app.user.common.UserInfoData_Sync.setUserInfo(UserInfoData_Sync.java:508)  
  9.         at com.chinahh.app.user.common.UserInfoData_Sync.writeAsync(UserInfoData_Sync.java:681)  
  10.         at com.chinahh.app.user.common.UserInfoData_Sync.access$000(UserInfoData_Sync.java:45)  
  11.         at com.chinahh.app.user.common.UserInfoData_Sync$5.run(UserInfoData_Sync.java:140)  
  12.         at com.chinahh.app.user.common.UserInfoData_Sync$5.run(UserInfoData_Sync.java:136)  
  13.         at com.chinahh.util.ThreadProc.dequeueAction(LazyQueue.java:234)  
  14.         at com.chinahh.util.ThreadProc.run(LazyQueue.java:190)  

 

通过该输出和代码的跟踪,定位到java应用的队列的代码是队列中的对象在等待出列,通过java应用逻辑来反推,对象出列时进行数据库操作,由于数据库数据量较大,在访问量较大的情况下可能会导致响应变慢。该队列是一个ConcurrentLinkedQueue非阻塞队列,初始容量是6W多,同时运行5个队列就是30多W个对象在队列中,并且单个对象也比较大,可以想象对内存的占用会相当大。到这里,问题就可以定位了。接下来就是问题的解决,具体步骤如下:

 

1.增大heap空间。

减轻GC线程的压力,看看是否有效。配置参数如下:

su - java -c "java -Xmx2g -Xms2g -verbose:gc -Xloggc:/var/log/java/test/gc/test_gc.log -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -jar /www/run/test-0.0.1-SNAPSHOT.jar nolocalcache & "

 

修改参数后,观察了一段时间发现,内存的使用基本控制住了,稳定在不到12G*20%,大概减少用了600M多的内存,对于我们那苦逼的服务器:12G内存,跑着10个java服务来说,应该说是不小的资源了。CPU的占用也有缩减,但不如内存那么明显,下周再继续跟踪。


2.减小队列的长度。

下周尝试将队列长度从6W缩小到3W,先看看效果。

 

对于telnet 不响应的分析,通过tcpdump查看该端口的请求,发现tcp的请求还一直在过来,但为什么本机telnet 测试报错呢:

telnet: connect to address : Connection timed out

 

是否由于GC太多频繁导致请求不响应,还是由于访问量较大(该服务日访问量峰值是4000多W),导致端口被使用殆尽呢,下周继续跟踪,期待能再有进展。

<think>好的,我现在需要帮用户解决在Windows上排查Java项目一直loading的问题。用户之前询问过如何手动释放MySQL连接,现在转向了Java项目的故障排查,可能他们在开发一个涉及数据库的Java应用时遇到了性能问题。 首先,用户提到的“一直loading”可能是指应用在启动或运行过程中卡住,无法继续执行。这种情况可能由多种原因引起,比如死锁、资源竞争、数据库连接问题、外部服务调用超时,或者代码中的死循环等。 我需要考虑用户可能的背景。他们可能是开发人员,对Java有一定了解,但可能不熟悉性能调优或故障排查工具。因此,我需要用简单明了的方法,分步骤指导他们如何排查问题,同时提供一些工具的使用方法,比如jstack、VisualVM等。 首先,需要确定问题发生的环节。是应用启动时卡住,还是运行到某个功能时卡住?如果是启动时,可能需要检查依赖加载、配置文件或数据库连接。如果是运行时,可能需要检查具体代码逻辑或外部服务调用。 接下来,检查日志。Java项目通常会有日志输出,查看是否有异常或错误信息,比如数据库连接超时,这关联到用户之前的问题,可能他们项目中有数据库连接泄漏,导致线程被占用,无法继续处理请求。这时候需要结合之前的MySQL连接释放方法,检查是否有未正确关闭的连接。 然后,使用jstack生成线程转储,分析线程状态。用户可能不知道如何分析线程转储,需要指导他们查找BLOCKED或WAITING状态的线程,特别是长时间运行的线程。此外,查找是否有死锁,jstack会自动报告死锁,如果有的话。 内存问题也可能导致应用卡顿,使用jmap或VisualVM检查内存使用情况,看是否有内存泄漏或频繁的GC活动。如果堆内存不足,应用可能会频繁进行垃圾回收,导致暂停时间过长,看起来像卡住。 数据库或外部服务的问题也不能忽视,比如慢查询或网络延迟。用户之前处理过MySQL连接问题,可能需要检查数据库查询是否优化,是否有未提交的事务或锁竞争,导致线程等待。 网络和IO操作同样重要,比如文件读写阻塞或网络调用超时。使用工具如Process Monitor可以查看文件系统活动,或检查网络连接状态。 代码层面的检查,尤其是多线程和同步代码,是否存在死锁或资源竞争。此外,是否有无限循环或递归调用,消耗CPU资源,导致应用无响应。 最后,建议用户使用VisualVM这样的图形化工具,实时监控应用状态,更容易定位问题。同时,提供一些常见的解决步骤,如重启应用、清理临时文件等,可能快速解决问题。 需要注意的是,用户可能希望得到逐步的指导,所以分步骤的方法会更清晰。同时,结合之前的MySQL问题,可能需要综合数据库和Java应用的排查,提醒用户检查数据库连接和查询性能。 总结下来,我需要组织一个结构化的回答,涵盖日志分析、线程分析内存检查、数据库和外部服务、网络IO、代码检查以及工具使用等方面,帮助用户系统地排查问题。同时,保持语言简洁,避免过于技术化的术语,确保用户能够理解并操作。</think>在 Windows 环境下排查 Java 项目长时间卡在 "loading" 状态的常见原因及方法如下: --- ### **1. 确定问题环节** - **启动阶段卡住**:检查依赖加载、配置文件、数据库连接池初始化等。 - **运行阶段卡住**:检查特定功能模块(如网络请求、文件读写、数据库查询等)。 --- ### **2. 检查日志** - **查看应用日志**:检查是否有报错或警告(如 `OutOfMemoryError`、数据库连接超时等)。 - **启用调试日志**:在启动命令中添加 `-Dlogging.level.root=DEBUG` 或配置日志框架(如 Log4j、Logback)输出更详细信息。 --- ### **3. 生成线程转储(Thread Dump)** 通过线程转储分析卡死的具体位置: #### **方法 1:使用 `jstack`** ```bash jstack -l <PID> > thread_dump.txt ``` - `<PID>` 是 Java 进程的进程 ID(通过 `jps` 或任务管理器查看)。 - 检查 `thread_dump.txt` 中是否有线程处于 `BLOCKED`、`WAITING` 或死锁状态。 #### **方法 2:使用 `jvisualvm`** 1. 运行 `jvisualvm`(JDK 自带工具,位于 `JDK/bin` 目录)。 2. 连接到目标 Java 进程,点击 **Threads** 标签,直接查看线程状态和堆栈信息。 --- ### **4. 内存分析** - **检查内存使用**: ```bash jmap -heap <PID> # 查看堆内存分配 jstat -gcutil <PID> 1000 # 每秒输出一次 GC 统计信息 ``` - **生成堆转储(Heap Dump)**: ```bash jmap -dump:format=b,file=heap_dump.hprof <PID> ``` 使用工具(如 Eclipse MAT、VisualVM)分析 `heap_dump.hprof`,查找内存泄漏或对象占用异常。 --- ### **5. 检查数据库/外部服务** - **数据库查询阻塞**: - 使用 `SHOW FULL PROCESSLIST`(MySQL)或类似命令检查是否有慢查询或未提交事务。 - 检查连接池配置(如最大连接数、超时时间)。 - **外部 API 调用超时**: - 检查网络是否通畅(如 `telnet <host> <port>`)。 - 确认外部服务响应时间是否过长。 --- ### **6. 网络/IO 操作排查** - **网络延迟**:使用 `Wireshark` 或 `tcpdump` 抓包分析网络请求。 - **文件读写阻塞**:检查文件路径权限、磁盘空间,或使用 `Process Monitor` 监控文件操作。 --- ### **7. 代码层面检查** - **死锁/线程竞争**:检查多线程代码中的 `synchronized` 块、`Lock` 使用是否合理。 - **无限循环/递归**:检查算法逻辑是否有死循环或未终止的递归调用。 - **资源未释放**:确认文件流、数据库连接等是否关闭(`try-with-resources`)。 --- ### **8. 使用性能分析工具** - **VisualVM**:监控 CPU内存、线程实时状态。 - **Async Profiler**:分析 CPU 占用和锁竞争。 - **Arthas**(阿里开源工具): ```bash # 使用 trace 命令追踪方法耗时 trace com.example.MyClass myMethod ``` --- ### **常见场景示例** #### **场景 1:数据库连接池耗尽** - **现象**:应用卡在数据库操作步骤。 - **排查**: 1. 检查连接池配置(如 `maxPoolSize`)。 2. 查看数据库活跃连接数是否达到上限。 3. 确保代码中正确释放连接(`close()` 方法调用)。 #### **场景 2:死锁** - **现象**:线程转储中显示多个线程互相等待锁。 - **解决**:优化锁的使用顺序,或用 `ReentrantLock` 替代 `synchronized`。 --- ### **快速恢复建议** 1. **重启应用**:临时释放资源(需结合日志分析根本原因)。 2. **清理临时文件**:如缓存文件或临时数据库锁文件。 3. **降级非核心功能**:关闭部分模块验证是否缓解问题。 通过以上步骤,可以逐步定位并解决 Java 项目卡在 "loading" 的问题。若问题复杂,建议结合日志、线程转储和性能工具综合分析
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值