1. 如何定位java內存泄露
1、為什麼會發生內存泄漏
Java如何檢測內在泄漏呢?我們需要一些工具進行檢測,並發現內存泄漏問題,不然很容易發生down機問題。
編寫java程序最為方便的地方就是我們不需要管理內存的分配和釋放,一切由jvm來進行處理,當java對象不再被應用時,等到堆內存不夠用時,jvm會進行垃圾回收,清除這些對象佔用的堆內存空間,如果對象一直被應用,jvm無法對其進行回收,創建新的對象時,無法從Heap中獲取足夠的內存分配給對象,這時候就會導致內存溢出。而出現內存泄露的地方,一般是不斷的往容器中存放對象,而容器沒有相應的大小限制或清除機制。容易導致內存溢出。
當伺服器應用佔用了過多內存的時候,如何快速定位問題呢?現在,Eclipse MAT的出現使這個問題變得非常簡單。EclipseMAT是著名的SAP公司貢獻的一個工具,可以在Eclipse網站下載到它,完全免費的。
要定位問題,首先你需要獲取伺服器jvm某刻內存快照。jdk自帶的jmap可以獲取內存某一時刻的快照,導出為dmp文件後,就可以用Eclipse MAT來分析了,找出是那個對象使用內存過多。
2、內存泄漏的現象:
常常地,程序內存泄漏的最初跡象發生在出錯之後,在你的程序中得到一個OutOfMemoryError。這種典型的情況發生在產品環境中,而在那裡,你希望內存泄漏盡可能的少,調試的可能性也達到最小。也許你的測試環境和產品的系統環境不盡相同,導致泄露的只會在產品中暴露。這種情況下,你需要一個低負荷的工具來監聽和尋找內存泄漏。同時,你還需要把這個工具同你的系統聯系起來,而不需要重新啟動他或者機械化你的代碼。也許更重要的是,當你做分析的時候,你需要能夠同工具分離而使得系統不會受到干擾。
一個OutOfMemoryError常常是內存泄漏的一個標志,有可能應用程序的確用了太多的內存;這個時候,你既不能增加JVM的堆的數量,也不能改變你的程序而使得他減少內存使用。但是,在大多數情況下,一個OutOfMemoryError是內存泄漏的標志。一個解決辦法就是繼續監聽GC的活動,看看隨時間的流逝,內存使用量是否會增加,如果有,程序中一定存在內存泄漏。
3、發現內存泄漏
1. jstat -gc pid
可以顯示gc的信息,查看gc的次數,及時間。
其中最後五項,分別是young gc的次數,young gc的時間,full gc的次數,full gc的時間,gc的總時間。
2.jstat -gccapacity pid
可以顯示,VM內存中三代(young,old,perm)對象的使用和佔用大小,
如:PGCMN顯示的是最小perm的內存使用量,PGCMX顯示的是perm的內存最大使用量,
PGC是當前新生成的perm內存佔用量,PC是但前perm內存佔用量。
其他的可以根據這個類推,OC是old內純的佔用量。
3.jstat -gcutil pid
統計gc信息統計。
4.jstat -gcnew pid
年輕代對象的信息。
5.jstat -gcnewcapacity pid
年輕代對象的信息及其佔用量。
6.jstat -gcold pid
old代對象的信息。
7.stat -gcoldcapacity pid
old代對象的信息及其佔用量。
8.jstat -gcpermcapacity pid
perm對象的信息及其佔用量。
9.jstat -class pid
顯示載入class的數量,及所佔空間等信息。
10.jstat -compiler pid
顯示VM實時編譯的數量等信息。
11.stat -printcompilation pid
當前VM執行的信息。
一些術語的中文解釋:
S0C:年輕代中第一個survivor(倖存區)的容量(位元組)
S1C:年輕代中第二個survivor(倖存區)的容量(位元組)
S0U:年輕代中第一個survivor(倖存區)目前已使用空間(位元組)
S1U:年輕代中第二個survivor(倖存區)目前已使用空間(位元組)
EC:年輕代中Eden(伊甸園)的容量(位元組)
EU:年輕代中Eden(伊甸園)目前已使用空間(位元組)
OC:Old代的容量(位元組)
OU:Old代目前已使用空間(位元組)
PC:Perm(持久代)的容量(位元組)
PU:Perm(持久代)目前已使用空間(位元組)
YGC:從應用程序啟動到采樣時年輕代中gc次數
YGCT:從應用程序啟動到采樣時年輕代中gc所用時間(s)
FGC:從應用程序啟動到采樣時old代(全gc)gc次數
FGCT:從應用程序啟動到采樣時old代(全gc)gc所用時間(s)
GCT:從應用程序啟動到采樣時gc用的總時間(s)
NGCMN:年輕代(young)中初始化(最小)的大小(位元組)
NGCMX:年輕代(young)的最大容量(位元組)
NGC:年輕代(young)中當前的容量(位元組)
OGCMN:old代中初始化(最小)的大小(位元組)
OGCMX:old代的最大容量(位元組)
OGC:old代當前新生成的容量(位元組)
PGCMN:perm代中初始化(最小)的大小(位元組)
PGCMX:perm代的最大容量(位元組)
PGC:perm代當前新生成的容量(位元組)
S0:年輕代中第一個survivor(倖存區)已使用的占當前容量百分比
S1:年輕代中第二個survivor(倖存區)已使用的占當前容量百分比
E:年輕代中Eden(伊甸園)已使用的占當前容量百分比
O:old代已使用的占當前容量百分比
P:perm代已使用的占當前容量百分比
S0CMX:年輕代中第一個survivor(倖存區)的最大容量(位元組)
S1CMX:年輕代中第二個survivor(倖存區)的最大容量(位元組)
ECMX:年輕代中Eden(伊甸園)的最大容量(位元組)
DSS:當前需要survivor(倖存區)的容量(位元組)(Eden區已滿)
TT:持有次數限制
MTT:最大持有次數限制
如果定位內存泄漏問題我一般使用如下命令:
Jstat -gcutil15469 2500 70
其中深藍色的部分就為內存泄漏的部分,java的堆內存一共只有481.5M而內存泄漏的部分獨自佔有了336.2M所以本次的內存泄漏很明顯,那麼我就來看看那個方法導致的內存泄漏:
從上圖我們可以發現紅線圈著的方法佔用了堆內存的67.75%,如果能把這個測試結果交給開發,開發是不是應該很好定位呢。所以作為一名高級測試工程師,我們需要學習的東西太多。
雖然不確定一定是內存泄漏,但是可以准確的告訴開發問題出現的原因,有一定的說服力。
2. 怎麼查看java代碼是否內存泄露
第一階段 通過jdk的GC輸出進行測試
可以在 JAVA_OPTS增加以下參數打開jdk的GC輸出日誌:
-verbose:gc -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError
打開輸出日誌,jdk會在每一次的垃圾回收時列印相關日誌
第二階段 通過jmap命令
jmap命令可以獲得運行中的jvm的堆的快照,從而可以離線分析堆,以檢查內存泄漏,檢查一些嚴重影響性能的大對象的創建,檢查系統中什麼對象最多,各種對象所佔內存的大小等等
第三階段 通過Eclipse Memory Analyzer 分析工具來分析
Eclipse Memory Analyzer是一種快速的,功能豐富的Java堆分析工具,以下簡稱MAT,可以幫助查找內存泄露,並減少內存消耗。 這個工具可以對由堆轉儲產生的數以億計的對象進行分析,一旦堆轉儲被解析,可以在打開他的一瞬間,立即得到保留大小的單一對象,提取記錄詳細的信息,查看為什麼這些對象對象資料沒有被釋放掉。使用這些功能的報告,可以對這些對象進行跟蹤,找到內存泄露嫌疑人,也可以得到系統的性能指數,幫助優化系統。
3. 如何排查Java內存泄露
1.打開/tomcat_home/bin/catalina.bat文件
2.加上:set JAVA_OPTS=%JAVA_OPTS% -server -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:heapmp,這樣當內存溢出是就會在對應路徑下生成mp文件
4. java內存診斷軟體
對於每一個java進程來說都有自己的內存池和使用空間,而這也就意味著會出現內存使用錯誤等問題,而這時候我們就需要對java內存進行診斷分析,今天雲南java培訓http://www.kmbdqn.cn/就一起來了就一下,在進行內存診斷上都有哪些軟體可以使用。
Java堆:分析診斷數據
堆轉儲分析
堆轉儲可以使用如下的工具進行分析:
EclipseMAT(內存分析工具,MemoryAnalyzerTool)是一個社區開發的分析堆轉儲的工具。它提供了一些很棒的特性,包括:
可疑的泄漏點:它能探測堆轉儲中可疑的泄露點,報告持續佔有大量內存的對象;
直方圖:列出每個類的對象數量、淺大小(shallow)以及這些對象所持有的堆。直方圖中的對象可以很容易地使用正則表達式進行排序和過濾。這樣有助於放大並集中我們懷疑存在泄露的對象。它還能夠對比兩個堆轉儲的直方圖,展示每個類在實例數量方面的差異。這樣能夠幫助我們查找Java堆中增長快的對象,並進一步探查確定在堆中持有這些對象的根;
不可達的對象:MAT有一個非常棒的功能,那就是它允許在它的工作集對象中包含或排除不可達/死對象。如果你不想查看不可達的對象,也就是那些會在下一次GC周期中收集掉的對象,只關心可達的對象,那麼這個特性是非常便利的;
重復的類:展現由多個類載入器所載入的重復的類;
到GC根的路徑:能夠展示到GC根(JVM本身保持存活的對象)的引用鏈,這些GC根負責持有堆中的對象;
OQL:我們可以使用對象查詢語言(ObjectQueryLanguage)來探查堆轉儲中的對象。它豐富了OQL的基礎設施,能夠編寫復雜的查詢,幫助我們深入了解轉儲的內部。
JavaVisualVM:監控、分析和排查Java語言的一站式工具。它可以作為JDK工具的一部分來使用,也可以從GitHub上下載。它所提供的特性之一就是堆轉儲分析。它能夠為正在監控的應用創建堆轉儲,也可以載入和解析它們。從堆轉儲中,它可以展現類的直方圖、類的實例,也能查找特定實例的GC根;
jhat命令工具(在/bin文件夾中)提供了堆轉儲分析的功能,它能夠在任意的瀏覽器中展現堆轉儲中的對象。默認情況下,Web伺服器會在7000埠啟動。jhat支持范圍廣泛的預定義查詢和對象查詢語言,以便於探查堆轉儲中的對象;
Java任務控制(JavaMissionControl)的JOverflow插件:這是一個實驗性的插件,能夠讓Java任務控制執行簡單的堆轉儲分析並報告哪裡可能存在內存浪費;
Yourkit是一個商業的Javaprofiler,它有一個堆轉儲分析器,具備其他工具所提供的幾乎所有特性。除此之外,YourKit還提供了:
可達性的范圍(reachabilityscope):它不僅能夠列出可達和不可達的對象,還能按照它們的可達性范圍顯示它們的分布,也就是,強可達、弱/軟可達或不可達;
內存探查:YourKit內置了一組全面的查詢,而不是使用ad-hoc查詢功能,YourKit的查詢能夠探查內存,查找反模式並為常見的內存問題分析產生原因和提供解決方案。
5. Windows 下有哪些內存泄露監測工具
1. ccmalloc-Linux和Solaris下對C和C++程序的簡單的使用內存泄漏和malloc調試庫。
2. Dmalloc-Debug Malloc Library.
3. Electric Fence-Linux分發版中由Bruce Perens編寫的malloc()調試庫。
4. Leaky-Linux下檢測內存泄漏的程序。
5. LeakTracer-Linux、Solaris和HP-UX下跟蹤和分析C++程序中的內存泄漏。
6. MEMWATCH-由Johan Lindh編寫,是一個開放源代碼C語言內存錯誤檢測工具,主要是通過gcc的precessor來進行。
7. Valgrind-Debugging and profiling Linux programs, aiming at programs written in C and C++.
8. KCachegrind-A visualization tool for the profiling data generated by Cachegrindand Calltree.
9. Leak Monitor-一個Firefox擴展,能找出跟Firefox相關的泄漏類型。
10. IE Leak Detector (Drip/IE Sieve)-Drip和IE Sieve leak detectors幫助網頁開發員提升動態網頁性能通過報告可避免的因為IE局限的內存泄漏。
11. Windows Leaks Detector-探測任何Win32應用程序中的任何資源泄漏(內存,句柄等),基於Win API調用鉤子。
12. SAP Memory Analyzer-是一款開源的JAVA內存分析軟體,可用於輔助查找JAVA程序的內存泄漏,能容易找到大塊內存並驗證誰在一直佔用它,它是基於Eclipse RCP(Rich Client Platform),可以下載RCP的獨立版本或者Eclipse的插件。
13. DTrace-即動態跟蹤Dynamic Tracing,是一款開源軟體,能在Unix類似平台運行,用戶能夠動態檢測操作系統內核和用戶進程,以更精確地掌握系統的資源使用狀況,提高系統性能,減少支持成本,並進行有效的調節。
14. IBM Rational PurifyPlus-幫助開發人員查明C/C++、託管.NET、Java和VB6代碼中的性能和可靠性錯誤。PurifyPlus 將內存錯誤和泄漏檢測、應用程序性能描述、代碼覆蓋分析等功能組合在一個單一、完整的工具包中。
15. Parasoft Insure++-針對C/C++應用的運行時錯誤自動檢測工具,它能夠自動監測C/C++程序,發現其中存在著的內存破壞、內存泄漏、指針錯誤和I/O等錯誤。並通過使用一系列獨特的技術(SCI技術和變異測試等),徹底的檢查和測試我們的代碼,精確定位錯誤的准確位置並給出詳細的診斷信息。能作為Microsoft Visual C++的一個插件運行。
16. Compuware DevPartner for Visual C++ BoundsChecker Suite-為C++開發者設計的運行錯誤檢測和調試工具軟體。作為Microsoft Visual Studio和C++ 6.0的一個插件運行。
17. Electric Software GlowCode-包括內存泄漏檢查,code profiler,函數調用跟蹤等功能。給C++和.Net開發者提供完整的錯誤診斷,和運行時性能分析工具包。
18. Compuware DevPartner Java Edition-包含Java內存檢測,代碼覆蓋率測試,代碼性能測試,線程死鎖,分布式應用等幾大功能模塊。
19. Quest JProbe-分析Java的內存泄漏。
20. ej-technologies JProfiler-一個全功能的Java剖析工具,專用於分析J2SE和J2EE應用程序。它把CPU、執行緒和內存的剖析組合在一個強大的應用中。JProfiler可提供許多IDE整合和應用伺服器整合用途。JProfiler直覺式的GUI讓你可以找到效能瓶頸、抓出內存泄漏、並解決執行緒的問題。4.3.2注冊碼:A-G666#76114F-1olm9mv1i5uuly#0126
21. BEA JRockit-用來診斷Java內存泄漏並指出根本原因,專門針對Intel平台並得到優化,能在Intel硬體上獲得最高的性能。
22. SciTech Software AB .NET Memory Profiler-找到內存泄漏並優
6. 怎麼排查這些內存泄漏
最原始的內存泄露測試
重復多次操作關鍵的可疑的路徑,從內存監控工具中觀察內存曲線,是否存在不斷上升的趨勢且不會在程序返回時明顯回落。
這種方式可以發現最基本,也是最明顯的內存泄露問題,對用戶價值最大,操作難度小,性價比極高。
MAT內存分析工具
2.1 MAT分析heap的總內存佔用大小來初步判斷是否存在泄露
在Devices 中,點擊要監控的程序。
點擊Devices視圖界面中最上方一排圖標中的「Update Heap」
點擊Heap視圖
點擊Heap視圖中的「Cause GC」按鈕
到此為止需檢測的進程就可以被監視。Heap視圖中部有一個Type叫做data object,即數據對象,也就是我們的程序中大量存在的類類型的對象。在data object一行中有一列是「Total Size」,其值就是當前進程中所有Java數據對象的內存總量,一般情況下,這個值的大小決定了是否會有內存泄漏。可以這樣判斷:
進入某應用,不斷的操作該應用,同時注意觀察data object的Total Size值,正常情況下Total Size值都會穩定在一個有限的范圍內,也就是說由於程序中的的代碼良好,沒有造成對象不被垃圾回收的情況。
所以說雖然我們不斷的操作會不斷的生成很多對象,而在虛擬機不斷的進行GC的過程中,這些對象都被回收了,內存佔用量會會落到一個穩定的水平;反之如果代碼中存在沒有釋放對象引用的情況,則data object的Total Size值在每次GC後不會有明顯的回落。隨著操作次數的增多Total Size的值會越來越大,直到到達一個上限後導致進程被殺掉。
2.2 MAT分析hprof來定位內存泄露的原因所在。
這是出現內存泄露後使用MAT進行問題定位的有效手段。
A)Dump出內存泄露當時的內存鏡像hprof,分析懷疑泄露的類:
B)分析持有此類對象引用的外部對象
C)分析這些持有引用的對象的GC路徑
D)逐個分析每個對象的GC路徑是否正常
從這個路徑可以看出是一個antiRadiationUtil工具類對象持有了MainActivity的引用導致MainActivity無法釋放。此時就要進入代碼分析此時antiRadiationUtil的引用持有是否合理(如果antiRadiationUtil持有了MainActivity的context導致節目退出後MainActivity無法銷毀,那一般都屬於內存泄露了)。
2.3 MAT對比操作前後的hprof來定位內存泄露的根因所在。
為查找內存泄漏,通常需要兩個 Dump結果作對比,打開 Navigator History面板,將兩個表的 Histogram結果都添加到 Compare Basket中去
A) 第一個HPROF 文件(usingFile > Open Heap Dump ).
B)打開Histogram view.
C)在NavigationHistory view里 (如果看不到就從Window >show view>MAT- Navigation History ), 右擊histogram然後選擇Add to Compare Basket .
D)打開第二個HPROF 文件然後重做步驟2和3.
E)切換到Compare Basket view, 然後點擊Compare the Results (視圖右上角的紅色」!」圖標)。
F)分析對比結果
可以看出兩個hprof的數據對象對比結果。
通過這種方式可以快速定位到操作前後所持有的對象增量,從而進一步定位出當前操作導致內存泄露的具體原因是泄露了什麼數據對象。
注意:
如果是用 MAT Eclipse 插件獲取的 Dump文件,不需要經過轉換則可在MAT中打開,Adt會自動進行轉換。
而手機SDk Dump 出的文件要經過轉換才能被 MAT識別,android SDK提供了這個工具 hprof-conv (位於 sdk/tools下)
首先,要通過控制台進入到你的 android sdk tools 目錄下執行以下命令:
./hprof-conv xxx-a.hprof xxx-b.hprof
例如 hprof-conv input.hprof out.hprof
此時才能將out.hprof放在eclipse的MAT中打開。
手機管家內存泄露每日監控方案
目前手機管家的內存泄露每日監控會自動運行並輸出是否存在疑似泄露的報告郵件,不論泄露對象的大小。這其中涉及的核心技術主要是AspectJ,MLD自研工具(原理是虛引用)和UIAutomator。
3.1 AspectJ插樁監控代碼
手機管家目前使用一個ant腳本加入MLD的監控代碼,並通過AspectJ的語法實現插樁。
使用AspectJ的原因是可以靈活分離出項目源碼與監控代碼,通過不同的編譯腳本打包出不同用途的安裝測試包:如果測試包是經過Aspect插樁了MLD監控代碼的話,那麼運行完畢後會輸出指定格式的日誌文件,作為後續分析工作的數據基礎。
3.2 MLD實現監控核心邏輯
這是手機管家內的一個工具工程,正式打包不會打入,BVT等每日監控測試包可以打入。打入後可以通過諸如addObject介面(通過反射去檢查是否含有該工具並調用)來加入需要監控的檢測對象,這個工具會自動在指定時機(如退出管家)去檢測該對象是否發生泄漏。
這個內存泄露檢測的基本原理是:
虛引用主要用來跟蹤對象被垃圾回收器回收的活動。虛引用必須和引用隊列(ReferenceQueue)聯合使用(在虛引用函數就必須關聯指定)。當垃圾回收器准備回收一個對象時,如果發現它還有虛引用,就會在回收對象的內存之前,自動把這個虛引用加入到與之關聯的引用隊列中。程序可以通過判斷引用隊列中是否已經加入了虛引用,來了解被引用的對象是否將要被垃圾回收。
基於以上原理,MLD工具在調用介面addObject加入監控類型時,會為該類型對象增加一個虛引用,注意虛引用並不會影響該對象被正常回收。因此可以在ReferenceQueue引用隊列中統計未被回收的監控對象是否超過指定閥值。
利用PhantomReferences(虛引用)和ReferenceQueue(引用隊列),當PhantomReferences被加入到相關聯的ReferenceQueue時,則視該對象已經或處於垃圾回收器回收階段了。
MLD監控原理核心
目前手機管家已對大部分類完成內存泄露的監控,包括各種activity,service和view頁面等,務求在技術上能帶給用戶最順滑的產品體驗。
接下來簡單介紹下這個工具的判斷核心。根據虛引用監控到的內存狀態,需要通過多種策略來判斷是否存在內存泄露。
(1)最簡單的方式就是直接在加入監控時就為該類型設定最大存在個數,舉個例子,各個DAO對象理論上只能存在最多一個,因此一旦出現兩個相同的DAO,那一般都是泄露了;
(2)第二種情況是在頁面退出程序退出時,檢索gc後無法釋放的對象列表,這些對象類型也會成為內存泄露的懷疑對象;
(3)最後一種情況比較復雜,基本原理是根據歷史操作判斷對象數量的增長幅度。根據對象的增長通過最小二乘法擬合出該對象類型的增長速度,如果超過經驗值則會列入疑似泄露的對象列表。
3.3 UIAutomator完成重復操作的自動化
最後一步就很簡單了。這么多反復的UI操作,讓人工來點就太浪費人力了。我們使用UIAutomator來進行自動化操作測試。
目前手機管家的每日自動化測試已覆蓋各個功能的主路徑,並通過配置文件的方式來靈活驅動用例的增刪改查,最大限度保證了隨著版本推移用例的復用價值。
至此手機管家的內存泄露測試方案介紹完畢,也歡迎各路牛人交流溝通更多更強的內存泄露工具盒方案!
騰訊Bugly簡介
Bugly是騰訊內部產品質量監控平台的外發版本,其主要功能是App發布以後,對用戶側發生的Crash以及卡頓現象進行監控並上報,讓開發同學可以第一時間了解到App的質量情況,及時機型修改。目前騰訊內部所有的產品,均在使用其進行線上產品的崩潰監控。