天天看點

堆轉儲上的線程:_妖精陷阱:線程轉儲分析模式

從細鹽到複雜的宇宙,到處都有圖案。 真實的模式可以承受時間。 在瞬息萬變的世界中,它們是不變的。 它需要大量的辛勤工作,豐富的經驗(包括好的和壞的),雷射聚焦和毅力,才能使噪聲結晶并建立圖案。 幸運的是,我們的計算機世界很幸運能夠建立出如此出色的模式。

經典示例之一是軟體設計模式:由Erich Gamma,Richard Helm,Ralph Johnson和John Vlissides建立的單例,工廠,訪客,觀察者,紀念品等。 這四位工程師總結了他們多年的學習經驗,對它們進行了精煉,并将它們作為易于了解的模式傳遞給我們。 無論您使用哪種程式設計語言(Java,C,C ++,PHP,Ruby等),無論運作哪種技術堆棧(JEE,.NET,LAMP等),并且無論使用哪種程式設計語言,這些模式都是通用的。您建構哪種類型的應用程式(移動,Web,SOA,微服務,批處理等)。

線程轉儲分析模式

在這四位偉大的工程師以我有限的能力的啟發下,以謙卑的方式,通過我多年的生産戰役,線程轉儲分析模式得以結晶,完善和創造。 線程轉儲是RCA(根本原因分析)的重要組成部分。 您的應用程式突然變得無響應了嗎? 您的應用程式的CPU是否在沒有增加流量,沒有進行任何代碼更改或任何環境更改的情況下開始出現峰值? 應用程式的響應是否開始下降? 您的應用程式運作數天/周後是否開始遇到記憶體問題? 線程轉儲中提供了幾個此類複雜問題的答案。 但是它們被埋在了許多細節之中。 為了闡明這些隐藏的答案,我建立了線程轉儲分析模式。 在本文中,讓我向您介紹“妖精陷阱”模式。 擁有finalize()方法的對象在垃圾回收期間的處理方式與沒有那些方法的對象不同。 在垃圾回收期間,帶有finalize()的對象不會立即從記憶體中清除。 而是,第一步,将這些對象添加到java.lang.ref.Finalizer對象的内部隊列中。 名為“ Finalizer”的低優先級JVM線程執行隊列中每個對象的finalize()方法。 隻有在執行finalize()之後,對象才有資格使用GC。 由于finalize()的實作不佳,如果終結器線程被阻塞,那麼它将對JVM産生嚴重的,有害的級聯影響。 如果Finalizer被阻止,則java.lang.ref.Finalize的内部隊列将開始增長。 這将導緻JVM的記憶體消耗快速增長。 然後,它将導緻OutOfMemoryError,進而危及整個JVM的可用性。 是以,在分析線程轉儲時,強烈建議研究Finalizer線程的堆棧跟蹤。

真實示例

這是被阻止的終結器線程的示例堆棧跟蹤:

"Finalizer" daemon prio=10 tid=0x00007fb2dc32b000 nid=0x7a21 waiting for monitor entry [0x00007fb2cdcb6000]
   java.lang.Thread.State: BLOCKED (on object monitor)
at net.sourceforge.jtds.jdbc.JtdsConnection.releaseTds(JtdsConnection.java:2024)
- waiting to lock 0x00000007d50d98f0 (a net.sourceforge.jtds.jdbc.JtdsConnection)
at net.sourceforge.jtds.jdbc.JtdsStatement.close(JtdsStatement.java:972)
at net.sourceforge.jtds.jdbc.JtdsStatement.finalize(JtdsStatement.java:219)
at java.lang.ref.Finalizer.invokeFinalizeMethod(Native Method)
at java.lang.ref.Finalizer.runFinalizer(Finalizer.java:101)
at java.lang.ref.Finalizer.access$100(Finalizer.java:32)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:178)      
The above stack trace was captured from a JVM that was using one of the older versions of the JTDS JDBC driver. Apparently, this version of the driver had an issue; you can see the finalize() method in the net.sourceforge.jtds.jdbc.JtdsStatement object calling the JtdsConnection#releaseTds() method. Apparently, this method got blocked and never returned back. Thus, the Finalizer thread got stuck indefinitely in the JtdsConnection#releaseTds() method. Due to that, Finalizer wasn’t able to work on the other objects that had finalize() methods. Due to that, the application started to suffer from an OutOfMemoryError . In the latest version of the JTDS JDBC driver, this issue was fixed. The point is that when you are implementing finalize() methods, be very careful.

      

為什麼命名為妖精陷阱?

西方一些國家的孩子們在慶祝聖帕特裡克節的過程中建造了妖精陷阱。 妖精是童話人物-基本上是一個很小的老人,穿着綠色的外套和帽子,ard積并尋找金币。 孩子們用金币吸引小妖精的創意陷阱。 同樣,焦慮的終結器線程總是在搜尋具有finalize()方法來執行它們的對象。 如果finalize()方法實作錯誤,則可能會捕獲Finalizer線程。 由于這種相似性,我們将其命名為妖精陷阱。

工具

您可以在此處了解更多此類模式 。 另一方面,為了使使用者輕松使用,我們建構了一個通用的線程轉儲分析工具: fastthread.io ,其中合并了所有線程轉儲分析模式。 您隻需将線程轉儲上載到此聯機工具,即可自動應用所有線程轉儲分析模式,并且将在瞬間将問題的根本原因報告給您。

翻譯自: https://www.theserverside.com/blog/Coffee-Talk-Java-News-Stories-and-Opinions/The-Leprechaun-Trap-A-Thread-Dump-Analysis-Pattern