tomcat cpu佔用過高,系統負載高問題跟蹤
2011-09-06 線上8核 linux伺服器,負載為8為正常情況,目前CPU負載過高,最高負載30多,平均負載在20左右,已經持續近一週,具體佔用CPU資源的服務是tomcat_sc,佔用CPU資源高達:720% 使用jconsole去跟蹤 更改catalina.sh 啟動設定:
$ CATALINA_OPTS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=8933 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=$server_ip";
import java.rmi.registry.LocateRegistry; import javax.management.MBeanServer; import javax.management.remote.JMXConnectorServer; import javax.management.remote.JMXConnectorServerFactory; import javax.management.remote.JMXServiceURL; import java.io.IOException; import java.lang.management.ManagementFactory; import java.net.MalformedURLException; public class JmxTest { public static void main(String[] args) { MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer(); JMXServiceURL url = null; try { url = new JMXServiceURL( "service:jmx:rmi://localhost:12199/jndi/rmi://localhost:8933/jmxrmi"); } catch (MalformedURLException e) { e.printStackTrace(); } JMXConnectorServer connectorServer = null; try { connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbeanServer); } catch (IOException e) { e.printStackTrace(); } try { System.setProperty("java.rmi.server.randomIDs", "true"); LocateRegistry.getRegistry(8933); connectorServer.start(); } catch (IOException e) { e.printStackTrace(); } } }
hostname -i 為127.0.0.1 測試伺服器配置完後,在本機使用jconsole連線,輸入測試伺服器帳號密碼即可連上。網上資料說要改hostname,沒有更改hostname也可行 線上先開了伺服器埠,又開了 本機埠,telnet可以連上,但jconsole無法連線,查google,說: the jvm you're trying to connect to actually exposes *two* ports, the one specified via -Dcom.sun.management.jmxremote.port, and some other one. The 2nd one is random, but jconsole wants to connect to it, so if you have a firewall, and you've only opened up the above port, you're hosed. 只開放了一個埠就不可以?必須外網伺服器所有埠都對內網開放?繼續跟蹤。 jmap jconsole jstack都是java自帶的jmx 問題跟蹤工具, 可以學習一下幫助分析定位記憶體溢位 程式死鎖之類的程式問題 使用 jmap 檢視記憶體狀況 jmap -histo:live pid 服務自建類的數量並不多 jstack 跟蹤堆疊也沒看出個所以然 繼續jconsole調查 google “jconsole remote set random port to certain” 找到一篇像樣的文章: http://www.componative.com/content/controller/developer/insights/jconsole3/ 於是寫了servlet去註冊指定埠 未果 在測試機上先試試 寫java檔案: 出現 java.rmi.AccessException: Cannot modify this registry 錯誤 註釋掉 catalina.sh的啟動設定 -Dcom.sun.management.jmxremote.port=8933 也不行 最終放棄了jconsole 使用 java.lang.management..ThreadMXBean 用焱哥轉發 新陽提供的 jsp頁面 分析效能問題,主要是看執行緒阻塞情況 主要程式碼:
ThreadMXBean tm = ManagementFactory.getThreadMXBean();
tm.setThreadContentionMonitoringEnabled(true);
<%
long [] tid = tm.getAllThreadIds();
ThreadInfo [] tia = tm.getThreadInfo(tid, Integer.MAX_VALUE);
long [][] threadArray = new long[tia.length][2];
for (int i = 0; i < tia.length; i++) {
long threadId = tia[i].getThreadId();
long cpuTime = tm.getThreadCpuTime(tia[i].getThreadId())/(1000*1000*1000);
threadArray[i][0] = threadId;
threadArray[i][1] = cpuTime;
}
檢測到如下執行緒問題: Thread ID: 89 Thread Name: http-6080-Processor73 Thread State: RUNNABLE Thread Lock Name: null Thread Lock Owner Name: null Thread CPU Time: 35678 sec Stack Info: (depth:31) +java.util.HashMap.get(HashMap.java:303) +com.netqin.baike.server.nqrs.CloudSecurityCommand.writePkgsLog(CloudSecurityCommand.java:466) +com.netqin.baike.server.nqrs.CloudSecurityCommand.execute(CloudSecurityCommand.java:153) +com.netqin.baike.server.BaikeServer.service(BaikeServer.java:64) +sun.reflect.GeneratedMethodAccessor33.invoke(Unknown Source) CPU佔用時間達到 35678秒 ,到下午到了50000秒左右,tomcat的CPU佔用達到了200% 分析程式碼,發現是單例bean中使用了 hashmap 作為類物件,多執行緒訪問時 類成員hashmap並不是執行緒安全的 非單例,引起了問題。更正程式碼後,幾個月沒有發現問題