本文目錄一覽:
- 1、怎麼排查這些內存泄漏
- 2、java內存泄漏怎麼解決
- 3、如何識別Java中的內存泄漏
- 4、如何排查Java內存泄露
- 5、如何定位java內存泄露
- 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 viewMAT- 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的質量情況,及時機型修改。目前騰訊內部所有的產品,均在使用其進行線上產品的崩潰監控。
java內存泄漏怎麼解決
總結網路、書本中的相關知識,介紹如何避免內存泄漏、溢出
首先介紹一下什麼是內存泄漏、溢出(參考我的收藏):
1、內存泄漏 memory leak:對象可達但不可用;是指程序在申請內存後,無法釋放已申請的內存空間,一次內存泄露危害可以忽略,但內存泄露堆積後果很嚴重,無論多少內存,遲早會被佔光。
2、內存溢出 out of memory:內存大小不夠;是指程序在申請內存時,沒有足夠的內存空間供其使用,出現out of memory;比如申請了一個integer,但給它存了long才能存下的數,那就是內存溢出。
如何避免內存泄漏、溢出
1、儘早釋放無用對象的引用
好的辦法是使用臨時變數的時候,讓引用變數在推出活動域後自動設置為null,暗示垃圾收集器來收集該對象,防止發生內存泄漏。
2、程序進行字元串處理時,盡量避免使用String,而應該使用StringBuffer。
因為String類是不可變的,每一個String對象都會獨立佔用內存一塊區域。
3、盡量少用靜態變數
因為靜態變數是全局的,存在方法區,GC不會回收。(用永久代實現的方法區,垃圾回收行為在這個區域是比較少出現的,垃圾回收器的主要目標是針對常量池和類型的卸載)
4、避免集中創建對象,尤其是大對象,如果可以的話盡量使用流操作
JVM會突然需要大量neicun,這時會出發GC優化系統內存環境
5、盡量運用對象池技術以提高系統性能
生命周期長的對象擁有生命周期短的對象時容易引發內存泄漏,例如大集合對象擁有大數據量的業務對象的時候,可以考慮分塊進行處理,然後解決一塊釋放一塊的策略。
6、不要在經常調用的方法中創建對象,尤其忌諱在循環中創建對象
可以適當的使用hashtable,vector創建一組對象容器,然後從容器中去取這些對象,而不用每次new之後又丟棄。
7、優化配置
內存溢出的解決方案是什麼:
1、從代碼層面進行優化完善,盡量避免該情況發生;
2、調整優化伺服器配置:
1)設置-Xms、-Xmx等
2)設置NewSize、MaxNewSize相等
3)設置 Heap size,PermGen space
如何識別Java中的內存泄漏
一般來說內存泄漏有兩種情況。一種情況,在堆中的分配的內存,在沒有將其釋放掉的時候,就將所有能訪問這塊內存的方式都刪掉(如指針重新賦值);另一種情況則是在內存對象明明已經不需要的時候,還仍然保留著這塊內存和它的訪問方式(引用)。第一種情況,在Java中已經由於垃圾回收機制的引入,得到了很好的解決。所以,Java中的內存泄漏,主要指的是第二種情況。
可能光說概念太抽象了,大家可以看一下這樣的例子:
1
Vector
v=new
Vector(10);
2
for
(int
i=1;i100;
i++){
3
Object
o=new
Object();
4
v.add(o);
5
o=null;
6
}
在這個例子中,代碼棧中存在Vector對象的引用v和Object對象的引用o。在For循環中,我們不斷的生成新的對象,然後將其添加到Vector對象中,之後將o引用置空。問題是當o引用被置空後,如果發生GC,我們創建的Object對象是否能夠被GC回收呢?答案是否定的。因為,GC在跟蹤代碼棧中的引用時,會發現v引用,而繼續往下跟蹤,就會發現v引用指向的內存空間中又存在指向Object對象的引用。也就是說儘管o引用已經被置空,但是Object對象仍然存在其他的引用,是可以被訪問到的,所以GC無法將其釋放掉。如果在此循環之後,Object對象對程序已經沒有任何作用,那麼我們就認為此Java程序發生了內存泄漏。
儘管對於C/C++中的內存泄露情況來說,Java內存泄露導致的破壞性小,除了少數情況會出現程序崩潰的情況外,大多數情況下程序仍然能正常運行。但是,在移動設備對於內存和CPU都有較嚴格的限制的情況下,Java的內存溢出會導致程序效率低下、佔用大量不需要的內存等問題。這將導致整個機器性能變差,嚴重的也會引起拋出OutOfMemoryError,導致程序崩潰。
如何排查Java內存泄露
1.打開/tomcat_home/bin/catalina.bat文件
2.加上:set JAVA_OPTS=%JAVA_OPTS% -server -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\heapdump,這樣當內存溢出是就會在對應路徑下生成dump文件
運行程序打開jdk bin 文件夾下再帶的 jvisualvm.exe
選擇tomcat線程,打開實時監控頁面可以看到對應的堆棧線程和裝在類,內存的實時情況
運行程序打開jdk bin 文件夾下再帶的 jconsole.exe,可以概覽堆棧線程和裝在類,內存的全部運行時間情況
下載安裝mat,dump分析軟體,安裝後,點擊file open 導入dump文件
打開後,灰色區域為可能發生內存溢出的區域,下帶問題描述
選擇Histogram,點擊shallow列進行排序,找出實例最多的
右鍵實例最的選擇list objects-with incoming references,可在根據正則表達式輸入自己想查的類,搜索,後右鍵打開 path to gc root-exclude phantom/weak/soft etc. references 就可以查出 調用為回收的相關信息。
如何定位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
[root@ssss logs]# jstat -gcutil 15469 1000 300
S0 S1 E O P YGC YGCT FGC FGCT GCT
0.00 1.46 26.54 4.61 30.14 35 0.872 0 0.000 0.872
0.00 1.46 46.54 4.61 30.14 35 0.872 0 0.000 0.872
0.00 1.46 47.04 4.61 30.14 35 0.872 0 0.000 0.872
0.00 1.46 65.19 4.61 30.14 35 0.872 0 0.000 0.872
0.00 1.46 67.54 4.61 30.14 35 0.872 0 0.000 0.872
0.00 1.46 87.54 4.61 30.14 35 0.872 0 0.000 0.872
0.00 1.46 88.03 4.61 30.14 35 0.872 0 0.000 0.872
1.48 0.00 5.56 4.62 30.14 36 0.874 0 0.000 0.874
1000 代表多久間隔顯示一次,
100 代表顯示一次。
S0 — Heap上的 Survivor space 0 區已使用空間的百分比
S1 — Heap上的 Survivor space 1 區已使用空間的百分比
E — Heap上的 Eden space 區已使用空間的百分比
O — Heap上的 Old space 區已使用空間的百分比
P — Perm space 區已使用空間的百分比
YGC — 從應用程序啟動到採樣時發生 Young GC 的次數
YGCT– 從應用程序啟動到採樣時 Young GC 所用的時間(單位秒)
FGC — 從應用程序啟動到採樣時發生 Full GC 的次數
FGCT– 從應用程序啟動到採樣時 Full GC 所用的時間(單位秒)
GCT — 從應用程序啟動到採樣時用於垃圾回收的總時間(單位秒)
如果有大量的FGC就要查詢是否有內存泄漏的問題了,圖中的FGC數量就比較大,並且執行時間較長,這樣就會導致系統的響應時間較長,如果對jvm的內存設置較大,那麼執行一次FGC的時間可能會更長。
如果為了更好的證明FGC對伺服器性能的影響,我們可以使用java visualVM來查看一下:
從上圖可以發現執行FGC的情況,下午3:10分之前是沒有FGC的,之後出現大量的FGC。
上圖是jvm堆內存的使用情況,下午3:10分之前的內存回收還是比較合理,但是之後大量內存無法回收,最後導致內存越來越少,導致大量的full gc。
下面我們在看看大量full GC對伺服器性能的影響,下面是我用loadrunner對我們項目進行壓力測試相應時間的截圖:
從圖中可以發現有,在進行full GC後系統的相應時間有了明顯的增加,點擊率和吞吐量也有了明顯的下降。所以java內存泄漏對系統性能的影響是不可忽視的。
3、定位內存泄漏
當然通過上面幾種方法我們可以發現java的內存泄漏問題,但是作為一名合格的高級工程師,肯定不甘心就把這樣的結論交給開發,當然這也的結論交給開發,開發也很難定位問題,為了更好的提供自己在公司的地位,我們必須給開發工程師提供更深入的測試結論,下面就來認識一下MemoryAnalyzer.exe。java內存泄漏檢查工具利器。
首先我們必須對jvm的堆內存進行dump,只有拿到這個文件我們才能分析出jvm堆內存中到底存了些什麼內容,到底在做什麼?
MemoryAnalyzer的用戶我在這裡就不一一說明了,我的博客里也有說明,下面就展示我測試的成功圖:
其中深藍色的部分就為內存泄漏的部分,java的堆內存一共只有481.5M而內存泄漏的部分獨自佔有了336.2M所以本次的內存泄漏很明顯,那麼我就來看看那個方法導致的內存泄漏:
從上圖我們可以發現紅線圈著的方法佔用了堆內存的67.75%,如果能把這個測試結果交給開發,開發是不是應該很好定位呢。所以作為一名高級測試工程師,我們需要學習的東西太多。
雖然不確定一定是內存泄漏,但是可以準確的告訴開發問題出現的原因,有一定的說服力。
如何判斷內存泄漏
內存泄露是指使用內存完成後沒有釋放,內存增長並不能分辨增長出來的內存是進程真正要用的,還是進程泄露出來的。而CPU的佔用是瞬時的、確定的,不存在某個進程申請了CPU占著不用的情況。在穩定性測試(也叫持久測試或疲勞測試)中,需要觀察內存是否有泄露。然而使用內存的進程千千萬,整個伺服器的內存增長似乎也不能判斷某個進程的內存有泄露。因此在穩定性測試過程中往往需要全程關注指定進程的內存消耗,比如運行3天、7天。
查看內存使用情況的命令有ps、sar、svmon、vmstat等等,但本文並不從工具使用的角度來介紹,而是從性能測試中關注指標的角度來介紹。如果採用其他命令查看內存,需注意,相似的名字在不同命令當中的含義是不一樣的,一定要搞清楚這個欄位的真正含義。
例1:Virtual這個詞,有時候在內存裡面指Paging Space(換頁空間),有時指進程空間裡面佔用的所有分頁(包括物理內存和Paging Space中的分頁)。
例2:Nmon中的PgIn/PgOut、topas中的PageIn/PageOut是指對文件系統的換頁,而vmstat中的pi/po是對Paging Space的換頁,而topas P中進程的PAGE SPACE是指進程的Data Segment。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/186163.html