Linux服务器,占用CPU资源过高的JAVA代码定位

1. 背景

   在tomcat运行的过程中,由于开发人员不小心写的死循环代码,影响系统的稳定,导致系统停止响应,部分维护人员经验不足,无法定位问题的根源,常常用重启服务的方式来应急,但该方法治标不治本,运行一段时间后,仍然会出现上述问题,影响用户体验。
  本文利用java虚拟机自带的工具jstack命令,对问题代码进行定位与分析。

2. jstack简介

  jstack是java虚拟机自带的一种堆栈跟踪工具。jstack用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。

3. 操作步骤

  • (1)查看问题进程

在Linux系统的ssh中输入命令行中输入top,查看CPU占用情况 :

top

在这里插入图片描述
记录资源较高的java进程号ID为21502

  • (2)查找对应线程

命令行中输入如下信息,查看该进程对应的线程占用资源情况:

top -Hp 21502

在这里插入图片描述
找到资源占用最高的线程ID为21694,并将其换算为十六进制为54BE,一会用到。

  • (3)导出信息
    命令行中输入如下命令,将该进程信息导出到test.txt文件中
jstack  21502 >test.txt

并将生成的文件下载到本地。(此段代码可能会报错,如果报错请查看 第三章 常见问题)

  • (4)异常定位
    在test.txt文件中搜索刚才换算的十六进制54BE,定位到如下片段
"http-8888-3" daemon prio=10 tid=0x00002aacd1488800 nid=0x54be runnable [0x0000000041513000]
   java.lang.Thread.State: RUNNABLE
	at java.io.FileOutputStream.writeBytes(Native Method)
	at java.io.FileOutputStream.write(FileOutputStream.java:260)
	at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:65)
	at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:123)
	- locked <0x00002aaace722b00> (a java.io.BufferedOutputStream)
	at java.io.PrintStream.write(PrintStream.java:432)
	- locked <0x00002aaace666fe0> (a java.io.PrintStream)
	at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:202)
	at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:272)
	at sun.nio.cs.StreamEncoder.flushBuffer(StreamEncoder.java:85)
	- locked <0x00002aaace722968> (a java.io.OutputStreamWriter)
	at java.io.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:168)
	at java.io.PrintStream.write(PrintStream.java:477)
	- locked <0x00002aaace666fe0> (a java.io.PrintStream)
	at java.io.PrintStream.print(PrintStream.java:547)
	at java.io.PrintStream.println(PrintStream.java:686)
	- locked <0x00002aaace666fe0> (a java.io.PrintStream)
	at org.apache.tomcat.util.log.SystemLogHandler.println(SystemLogHandler.java:218)
	at dky.Test.test(Test.java:8)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at kd.ln.common.service.BeanTool.invokeMethod(BeanTool.java:179)
	at kd.ln.common.service.BeanTool.invokeMethod(BeanTool.java:142)
	at kd.ln.common.service.BeanTool.invokeMethod(BeanTool.java:130)
	at kd.ln.common.service.BeanTool.invokeMethod(BeanTool.java:125)
	at kd.ln.common.service.CommonService.invokeMethod(CommonService.java:53)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at flex.messaging.services.remoting.adapters.JavaAdapter.invoke(JavaAdapter.java:418)
	at flex.messaging.services.RemotingService.serviceMessage(RemotingService.java:183)
	at flex.messaging.MessageBroker.routeMessageToService(MessageBroker.java:1400)
	at flex.messaging.endpoints.AbstractEndpoint.serviceMessage(AbstractEndpoint.java:1005)
	at flex.messaging.endpoints.amf.MessageBrokerFilter.invoke(MessageBrokerFilter.java:103)
	at flex.messaging.endpoints.amf.LegacyFilter.invoke(LegacyFilter.java:158)
	at flex.messaging.endpoints.amf.SessionFilter.invoke(SessionFilter.java:44)
	at flex.messaging.endpoints.amf.BatchProcessFilter.invoke(BatchProcessFilter.java:67)
	at flex.messaging.endpoints.amf.SerializationFilter.invoke(SerializationFilter.java:166)
	at flex.messaging.endpoints.BaseHTTPEndpoint.service(BaseHTTPEndpoint.java:291)
	at flex.messaging.MessageBrokerServlet.service(MessageBrokerServlet.java:353)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:286)
	at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:845)
	at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
	at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
	at java.lang.Thread.run(Thread.java:619)

找到可能出现问题代码的位置,dky.Test.test(Test.java:8)

查找对应的java文件,发现存在死循环。

package dky;

public class Test {
    
    
	public static void test(String ss){
    
    
		int i=0;
		while(true){
    
    
			if(i%10000==0){
    
    
				System.out.println(i);
			}
			i++;
		}
	}
	public static void main(String[] args) {
    
    
		test("");
	}
	
}

4常见问题


在执行jstack命令时,可能会报如下错误

-bash: jstack: command not found

出现这种错误,一般情况是服务器没有配置环境变量,对于这种问题的解决方案为,找到jdk对应的路径,如/home/d5000/jdk/bin/,将路径加到命令行中即可。

/home/d5000/jdk/bin/./jstack 21502 >test.txt

猜你喜欢

转载自blog.csdn.net/pengain/article/details/112058917