jstack定位線程堆棧信息【案例彙總】

1.CPU利用率過高,代碼定位

 

找到CPU利用率持續比較高的進程, 命令:top

 

找到CPU使用率較高的線程ID(TID):

命令:ps p 16480 -L -o pcpu,pid,tid,time,tname,cmd

此處爲:16498

 

將獲取的線程號(十進制數)轉換成十六進制

printf "%x\n" 16498

結果:4072

結合進程號和線程號,利用jstack查到異常代碼所在行

jstack -l <pid> | grep <thread-hex-id> -A 10 命令顯示出錯的堆棧信息,如下圖:

jstack -l 16480| grep 0x4072 -A 10

 -A 10 參數用來指定顯示行數,否則只會顯示一行信息。

可以看到在代碼的第13行有問題。也就是說是這一句導致cpu佔用過高,在優化時,就可以針對這一部分代碼進行優化

 

 

2.響應時間過長,代碼定位及問題解決

 問題的起因是筆者在一輪性能測試的中,發現某協議的響應時間很長,去觀察哨兵監控裏的javamethod監控可以看到以下結果:

    a3227a73-c5a8-4847-b31a-6c4b655dcd3b?imageView&thumbnail=980x0

onMessage是該協議的總入口,可以看到該協議平均耗時爲352.11ms,觀察其他耗時方法可以看到updateUserForeignId耗時307.75ms,那麼可以認爲該方法的響應時間慢是該協議的最主要性能瓶頸,這時候我們應該看看該方法究竟做了哪些操作導致響應時間過長:

4edc9944-f9d6-4ff6-bc5d-ad00095cebf6?imageView&thumbnail=980x0

    從哨兵監控中可以看到userStorage.updateUserForeignId方法耗時122.86ms,userStorage.updateForeignIdPegging方法耗時71.33ms,這兩個方法有成爲了SessionProcessHelper.updateUserForeignId方法的主要耗時點,基於對代碼的熟悉程度xxxStroge方法都是一些數據庫的操作,那麼現在可以初步的認爲數據庫的相關操作可能是問題的根源所在,爲了清楚的展示問題,我們先選擇一個邏輯較簡單的方法分析一下,從上面的哨兵方法監控裏可以看到updateSession方法耗時34.88ms,查看該方法代碼:

d5d53feb-0183-4ac4-9fba-1c6bd4befcef

    可以看到方法只是有一個簡單的dao層的操作,通過查看dao層方法可知該方法僅僅是一個update操作,按常理來說這樣的操作需要三十多毫秒的耗時,顯然是偏長了,既然如此,我們繼續求根溯源利用哨兵的mqlcolletor來驗證一下該方法底層的sql操作究竟耗時多少毫秒。此處省略通過dao層方法查找sql語句的過程,直接來看結果:

97eb21f2-c20a-4ac8-9353-56865f978bab?imageView&thumbnail=980x0

    從這裏可以看到底層sql響應時間是1.62ms,而dao層方法耗時高達34.88ms,這裏顯然有問題的,這裏我們首先需要排查一下壓測機,數據庫的各資源指標是否到達瓶頸(在之前的性能測試中發現過類似的問題最後發現是數據庫機器的磁盤util接近100%,該機器性能較差導致出現該問題,後期更換數據庫機器解決了問題),通過檢查這些指標可以發現cpu,內存,網絡,磁盤各項指標均保持在正常水平。

   問題到這裏,貌似沒有什麼進展了,這時候就到了jstack登場了。在Java應用的性能測試中,很多性能問題可以通過觀察線程堆棧來發現,Jstack是JVM自帶dump線程堆棧的工具,很輕量易用,並且執行時不會對性能造成很大的影響。靈活的使用jstack可以發現很多隱祕的性能問題,是定位問題不可多得的好幫手。這裏推薦一下我們組內小寶哥的一篇關於jstack使用姿勢的一篇ks:巧用jstack定位性能問題

    我們來jstack一下,查看在測試執行的過程中,各線程所做的操作和線程狀態,可以看到以下狀態:

29493fef-4990-4dd0-9da7-07f0b45aefa7?imageView&thumbnail=980x0

    在所有的24個線程中,多執行幾次jstack可以發現大約有十個左右的線程處於waitting狀態,該狀態表明該線程正在執行obj.wait()方法,放棄了 Monitor,進入 “Wait Set”隊列,爲什麼阻塞住呢,繼續往下看堆棧信息,可以看到該線程正在做borrowobject操作,可以大概看到這裏是一個數據庫連接池的相關操作,具體到究竟是幹什麼的可以查看一些數據庫連接池的資料:dbcp源碼解讀與對象池原理剖析 https://blog.csdn.net/suixinm/article/details/41761019

    簡單的說一下,數據庫連接的使用過程:創建一個池對象工廠, 將該工廠注入到對象池中, 當要取池對象, 調用borrowObject, 當要歸還池對象時, 調用returnObject, 銷燬池對象調用clear(), 如果要連池對象工廠也一起銷燬, 則調用close()。從這裏可以很明顯的看到該線程waitting的原因是沒有獲取到連接池裏的連接對象,那麼很容易就可以想象的到導致該問題的原因是數據庫連接池比夠用導致的,於是將連接池的大小從10修改到了50,繼續執行一輪測試得到了以下結果:

1a3eb357-4330-4677-b635-ac0824eec6a0?imageView&thumbnail=980x0

    可以看到updateSession方法從34.88ms下降到20.13ms,雖然耗時下降了14ms,但是距離sql耗時的1.64ms仍然有差距,沿着剛剛的思路,我們繼續jstack一下,看看當前的線程狀態又是如何:

0c6ca4ac-3960-411e-9666-0b56abe854af

    在24個線程中平均下來會有十個左右的blocked狀態,繼續往下閱讀可以發現,該線程是blocked在了log4j.Category.callAppenders上,顯然可以發現這是個log4j的問題,那究竟爲何會阻塞在這裏呢,查看資料可以找到callAppenders的源碼(具體的log4j相關資料可以看這裏:Log4j 1.x版引發線程blocked死鎖問題):

/** 
     Call the appenders in the hierrachy starting at 
     this.  If no appenders could be found, emit a 
     warning. 
 
     
This method calls all the appenders inherited from the 
     hierarchy circumventing any evaluation of whether to log or not 
     to log the particular log request. 
 
     @param event the event to log.   
*/  
  public void callAppenders(LoggingEvent event) {  
    int writes = 0;  
  
    for(Category c = this; c != null; c=c.parent) {  
      // Protected against simultaneous call to addAppender, removeAppender,...  
      synchronized(c) {  
    if(c.aai != null) {  
      writes += c.aai.appendLoopOnAppenders(event);  
    }  
    if(!c.additive) {  
      break;  
    }  
      }  
    }  
  
    if(writes == 0) {  
      repository.emitNoAppenderWarning(this);  
    }  
  }
    

    我們從上面可以看出在該方法中有個synchronized同步鎖,同步鎖就會導致線程競爭,那麼在大併發情況下將會出現性能問題,同會引起線程BLOCKED問題。那麼如何優化log4j使其執行時間儘量短,防止線程阻塞呢,推薦一下我們組候姐的一篇文章:log4j不同配置對性能的影響    當前我們解決該問題的方式是去掉log4j配置文件中打印行號的選項,然後再執行一輪測試可以看到如下結果:    

80f598cf-f72f-4657-a743-06de7b63de08?imageView&thumbnail=980x0

可以看到響應updateSession響應時間又下降了一半到了11.48ms的水平,優化到這裏可以看到該dao層方法算是達到了一個正常水平,看總體的響應時間也從原先的352.11ms下降到109.19ms。可以認爲解決了該協議的性能問題。    總結下來,可以發現一個典型性能問題的分析思路:發現性能問題-->查看壓測機,服務器,數據庫資源指標是否達到瓶頸點-->結合哨兵找到主要耗時方法-->查看耗時方法的具體代碼邏輯-->具體問題具體分析(這裏是結合jstack查看堆棧信息)-->條件允許範圍內優化問題並驗證問題。
 

 

3.使用java命令jps和jstack快速定位線程狀態

線程狀態定義
在線上項目中,當程序處於長時間停頓的時候,可以使用java提供的jstack命令跟蹤正在執行方法的堆棧情況,jstack能夠生成虛擬機當前時刻的線程堆棧情況。主要,監控線程的狀態,判斷出線程停頓的原因。例如,死鎖,死循環,多個線程等待等等。線程的狀態包括NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED,其源碼如下:

public enum State {
 
        /**
         * Thread state for a thread which has not yet started.
         * 線程創建後尚未啓動的線程處於這種狀態
         */
        NEW,
 
        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * Runable包括了操作系統線程狀態中的Running和Ready, 也就是處於此
狀態的線程有可能正在執行, 也有可能正在等待着CPU爲它分配執行時間
         */
        RUNNABLE,
 
        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * 線程被阻塞了, “阻塞狀態”與“等待狀態”的區別是: “阻塞狀態”在等
待着獲取到一個排他鎖, 這個事件將在另外一個線程放棄這個鎖的時候發生; 而“等待狀
態”則是在等待一段時間, 或者喚醒動作的發生。 在程序等待進入同步區域的時候, 線程將
進入這種狀態。
         */
        BLOCKED,
 
        /**
         * Thread state for a waiting thread.
         * 無限期等待狀態,處於這種狀態的線程不會被分配CPU執行時間,它們要等待被
其他線程顯式地喚醒
         */
        WAITING,
 
        /**
         * Thread state for a waiting thread with a specified waiting time.
         * 線程等待被喚醒,處於這種狀態的線程也不會被分配CPU執行時間, 不過無
須等待被其他線程顯式地喚醒, 在一定時間之後它們會由系統自動喚醒
         */
        TIMED_WAITING,
 
        /**
         * Thread state for a terminated thread.
         * 線程已經執行結束
         */
        TERMINATED;
    }
線程之間的狀態轉換如圖:

線程狀態跟蹤
在Java中,使用jps命令,查詢正在運行的虛擬機java進程,一般顯示信息就是,pid和進程名稱。示例如下:


圖-2


 使用jstack [pid] 輸出當前進程的堆棧信息。主要有兩種使用方式,如下:

控制檯輸出堆棧信息 jstack pid,示例:

圖-3


將堆棧信息輸出到執行文件 jstack pid > file。示例,輸出pid 11840的進程堆棧信息存儲到dump11840文件中,執行命令jstack 11840 > C:\Users\86151\Desktop\dump11840。結果如下:

圖-4

圖-5
堆棧信息分析:示例堆棧信息如圖:


圖-6

 

  1. 線程名稱,ApplicationImpl pooled thred 450。
  2. 線程優先級。
  3. tid十六進制地址。
  4. 線程十六進制地址。
  5. 線程當前狀態,TIMED_WAITING。
  6. 線程當前執行的方法,park。


示例常見停頓場景
死鎖場景
在線程嵌套的獲取鎖,就有可能產生死鎖,如下給出死鎖的代碼示例,和堆棧信息。代碼示例如下:

public class DeadLockDemo {
 
    private static String a = "a";
    private static String b = "b";
 
    private void deadLock() {
        Thread t1 = new Thread(() -> {
            synchronized (a) {
 
                System.out.println("get a lock thread " + Thread.currentThread().getName());
                try {
                    // 延時2秒
                    Thread.sleep(2000L);
                } catch (Exception e) {
 
                }
                synchronized (b) {
                    System.out.println("get b lock thread " + Thread.currentThread().getName());
                }
            }
        });
 
        Thread t2 = new Thread(() -> {
            synchronized (b) {
 
                System.out.println("get b lock thread " + Thread.currentThread().getName());
                synchronized (a) {
 
                    System.out.println("get a lock thread " + Thread.currentThread().getName());
                }
            }
        });
        t1.start();
        t2.start();
    }
 
    public static void main(String[] args) {
 
        new DeadLockDemo().deadLock();
    }
}
程序運行後,線程t1獲取a的鎖,線程t2獲取b的鎖。然後,當線程2嘗試獲取a的鎖時,線程t1嘗試獲取b,由於此時a和b的鎖都沒有釋放,就產生了死鎖。執行的程序日誌輸出如下:

堆棧信息如下:

上圖中,代碼35行和代碼24行引起了死鎖。

長時間等待 
線程長時間,分配不到cpu而進入等待狀態。下面模擬一個線程在執行,大批量線程在等待的場景,示例代碼如下:

public class LongWaitDemo {
 
    private void longWait() {
 
        for (int i = 0; i < 100; i++) {
            Thread thread = new Thread(() -> {
                while (true) {
                    System.out.println("executing thread " + Thread.currentThread().getName());
                    try {
                        // 延時2000秒
                        Thread.sleep(2000000L);
                    } catch (Exception e) {
                    }
                }
            });
            thread.start();
        }
 
        Thread t2 = new Thread(() -> {
 
            while (true) {
                try {
                    // 延時1秒
                    Thread.sleep(1000L);
                } catch (Exception e) {
                }
                System.out.println("executing thread " + Thread.currentThread().getName());
            }
        });
        t2.start();
    }
 
    public static void main(String[] args) {
        new LongWaitDemo().longWait();
    }
}
跟蹤其堆棧信息,會發現幾乎全是TIMED_WAITING的狀態的信息,如圖:

 

 

4.tomcat應用無法啓動

問題現象:環境搭建時,部署應用後tomcat無法啓動,查看日誌並無報錯現象,直觀感覺tomcat啓動時好像卡在了哪裏,所以我們希望看到tomcat啓動時究竟發生了什麼,導致啓動無法完成,這時線程堆棧中的函數調用關係也許可以幫上忙,jstack得到對應tomcat應用的線程堆棧,如下:

"localhost-startStop-1" daemon prio=10 tid=0x0000000002da2000 nid=0x630b in Object.wait() [0x00007f1863538000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000007d6924800> (a org.apache.curator.framework.state.ConnectionStateManager)
        at java.lang.Object.wait(Object.java:503)
        at org.apache.curator.framework.state.ConnectionStateManager.blockUntilConnected(ConnectionStateManager.java:215)
        - locked <0x00000007d6924800> (a org.apache.curator.framework.state.ConnectionStateManager)
        at org.apache.curator.framework.imps.CuratorFrameworkImpl.blockUntilConnected(CuratorFrameworkImpl.java:223)
        at org.apache.curator.framework.imps.CuratorFrameworkImpl.blockUntilConnected(CuratorFrameworkImpl.java:229)
        at com.netease.kaola.kschedule.client.curator.CuratorSupport.initZK(CuratorSupport.java:81)
        at com.netease.kaola.kschedule.client.curator.CuratorSupport.check(CuratorSupport.java:293)
        - locked <0x00000007d64af060> (a java.lang.Class for com.netease.kaola.kschedule.client.curator.CuratorSupport)
        at com.netease.kaola.kschedule.client.curator.CuratorSupport.checkExists(CuratorSupport.java:113)
        at com.netease.kaola.kschedule.client.job.JobManagerImpl.isSchedulerStop(JobManagerImpl.java:218)
        at com.netease.kaola.kschedule.client.job.JobManagerImpl.startScheduler(JobManagerImpl.java:134)
        at com.netease.kaola.kschedule.client.KScheduleClientFactory.init(KScheduleClientFactory.java:90)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:606)
        ...

問題分析:首先關注線程狀態,是處於WATING(on object monitor),這時線程執行了Object.wait(),處於掛起狀態,在等待被喚醒,而且這裏並沒有設置超時時間,所以只要線程沒被喚醒,tomcat會一直等下去。但tomcat在等什麼呢,查看函數調用信息可以看到“com.netease.kaola.kschedule.client.curator.CuratorSupport.initZK”,這個函數是kschedule啓動時需要初始化zookeeper,應用啓動就是卡在了這裏。知道問題所在就好辦, 查看kschedule的配置,發現zookeeper的ip用的是私有ip,與應用不通,更改成機房ip後問題解決。

數據庫連接池不夠用導致響應時間久

問題現象:在測試一個場景時,發現響應時間很長,日誌也無報錯現象,根據調用鏈逐級定位,發現80%的時間都是消耗在DAO層的方法上,這時首先考慮的是sql會不會有問題?於是找DBA同學幫忙抓sql看下,但DBA同學反映sql執行很快,執行計劃也沒有問題,那問題出現在哪裏呢,找不到原因就看下線程堆棧,系統在dao層方法後做了什麼?jstack線程堆棧如下:

"DubboServerHandler-10.165.184.51:20881-thread-200" daemon prio=10 tid=0x00007f2fd6208800 nid=0x504b waiting on condition [0x00007f2fc0280000]
   java.lang.Thread.State: TIMED_WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x000000078172f2c0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:226)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2082)
        at com.alibaba.druid.pool.DruidDataSource.pollLast(DruidDataSource.java:1487)
        at com.alibaba.druid.pool.DruidDataSource.getConnectionInternal(DruidDataSource.java:1086)
        at com.alibaba.druid.pool.DruidDataSource.getConnectionDirect(DruidDataSource.java:953)
        at com.alibaba.druid.filter.FilterChainImpl.dataSource_connect(FilterChainImpl.java:4544)
        at com.alibaba.druid.filter.logging.LogFilter.dataSource_getConnection(LogFilter.java:827)
        at com.alibaba.druid.filter.FilterChainImpl.dataSource_connect(FilterChainImpl.java:4540)
        at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:931)
        at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:923)
        at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:100)
        at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:111)
        at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:77)
        at org.mybatis.spring.transaction.SpringManagedTransaction.openConnection(SpringManagedTransaction.java:81)
        at org.mybatis.spring.transaction.SpringManagedTransaction.getConnection(SpringManagedTransaction.java:67)
        at org.apache.ibatis.executor.BaseExecutor.getConnection(BaseExecutor.java:279)
        at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:72)
        at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:59)
        ...

問題分析:先關注線程狀態,發現堆棧信息裏大量的dubbo線程處於TIMED_WAITING狀態,從“waiting on condition”可以看出系統在等待一個條件發生,這時的線程處於sleep狀態,一般會有超時時間喚醒,一般出現TIMED_WAITING很正常,一些等待IO都會出現這種狀態,但是大量的TIMED_WAITING就要找原因了,觀察線程堆棧發現處於TIMED_WAITING狀態的線程都在等待druid獲取連接池的連接,這種現象很想連接池不夠用了,於是增加數據庫連接池的連接數,TPS直接提升了3倍。

線程阻塞導致響應變慢

問題現象:同樣是在測試場景時發現響應時間變慢,並且響應時間的毛刺現象比較嚴重,依次排查系統可能的瓶頸點沒有明顯收穫,這時jstack又排上用場了,先看線程堆棧:

"DubboServerHandler-10.165.184.34:20880-thread-199" daemon prio=10 tid=0x00007f3cb4196000 nid=0x6048 waiting for monitor entry [0x00007f3cabb79000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at org.apache.log4j.Category.callAppenders(Category.java:204)
    - waiting to lock <0x000000078c312718> (a org.apache.log4j.spi.RootLogger)
    at org.apache.log4j.Category.forcedLog(Category.java:391)
    at org.apache.log4j.Category.info(Category.java:666)
    at netease.qp.spellcheck.Facade.logInfo(Facade.java:26)
    at netease.qp.kaola.service.impl.SearchPhraseServiceImpl.getSearchGoodsResult(SearchPhraseServiceImpl.java:60)
    at com.alibaba.dubbo.common.bytecode.Wrapper1.invokeMethod(Wrapper1.java)
    at com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory$1.doInvoke(JavassistProxyFactory.java:46)
    at com.alibaba.dubbo.rpc.proxy.AbstractProxyInvoker.invoke(AbstractProxyInvoker.java:72)
    at com.alibaba.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:53)
    at com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter.invoke(TraceFilter.java:78)
    at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
    at com.alibaba.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:65)
    at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
    at com.alibaba.dubbo.rpc.filter.TimeoutFilter.invoke(TimeoutFilter.java:42)
    at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
    at com.alibaba.dubbo.rpc.filter.ExceptionFilter.invoke(ExceptionFilter.java:64)
    at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
    at com.alibaba.dubbo.rpc.filter.ContextFilter.invoke(ContextFilter.java:60)
    at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
    at com.alibaba.dubbo.rpc.filter.GenericFilter.invoke(GenericFilter.java:112)
    ...

問題分析:可以看到線程是處於BLOCKED狀態的,這種狀態我們需要重點關注,這時的線程是被阻塞的,進一步查看發現幾乎所有的dubbo線程都處於block狀態,都在“waiting to lock <0x000000078c312718>”,這個<0x000000078c312718>又是個什麼鬼?

Line 57:     - waiting to lock <0x000000078c312718> (a org.apache.log4j.spi.RootLogger)
    Line 119:     - waiting to lock <0x000000078c312718> (a org.apache.log4j.spi.RootLogger)
    Line 169:     - waiting to lock <0x000000078c312718> (a org.apache.log4j.spi.RootLogger)
    Line 207:     - waiting to lock <0x000000078c312718> (a org.apache.log4j.spi.RootLogger)
    Line 257:     - waiting to lock <0x000000078c312718> (a org.apache.log4j.spi.RootLogger)
    Line 295:     - waiting to lock <0x000000078c312718> (a org.apache.log4j.spi.RootLogger)
    Line 345:     - waiting to lock <0x000000078c312718> (a org.apache.log4j.spi.RootLogger)
    Line 407:     - waiting to lock <0x000000078c312718> (a org.apache.log4j.spi.RootLogger)
    Line 588:     - waiting to lock <0x000000078c312718> (a org.apache.log4j.spi.RootLogger)
    Line 686:     - waiting to lock <0x000000078c312718> (a org.apache.log4j.spi.RootLogger)
    Line 790:     - locked <0x000000078c312718> (a org.apache.log4j.spi.RootLogger)
    Line 840:     - waiting to lock <0x000000078c312718> (a org.apache.log4j.spi.RootLogger)
    Line 954:     - waiting to lock <0x000000078c312718> (a org.apache.log4j.spi.RootLogger)
    Line 995:     - waiting to lock <0x000000078c312718> (a org.apache.log4j.spi.RootLogger)
    Line 1105:     - waiting to lock <0x000000078c312718> (a org.apache.log4j.spi.RootLogger)
    Line 1143:     - waiting to lock <0x000000078c312718> (a org.apache.log4j.spi.RootLogger)
    Line 1197:     - waiting to lock <0x000000078c312718> (a org.apache.log4j.spi.RootLogger)
    ...

通過排查發現這個鎖是log4j拿到的,同時阻塞了其他線程通過log4j打日誌,Google類似問題才知道是log4j的一個bug,可以通過升級log4j版本或者精簡日誌避免,知道原因後經過相應的處理,性能得到大幅度提升,這裏安利一篇侯姐關於log4j優化的文章:http://doc.hz.netease.com/pages/viewpage.action?pageId=26313263

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章