1.工具準(zhǔn)備
本章節(jié)除了用到上一章提到的jcmd之外,還會(huì)使用到VisualVM工具。此工具在JDK安裝目錄\bin下,文件名為jvisualvm荧缘。你也可以在
http://visualvm.github.io/download.html
下載到最新的版本。
VisualVM使用各種技術(shù)(包括jvmstat拦宣,JMX胜宇,Serviceability Agent(SA)和Attach API)來進(jìn)行故障定位耀怜。至少需要具備JDK的版本1.4+。
我使用的是上述鏈接中下載的jvisualvm1.4版本桐愉。
官方中文教程:
http://visualvm.github.io/documentation.html
建議:將etc下visualvm配置文件中的-Xmx最大堆內(nèi)存的大小調(diào)大一些财破,否則在加載dump文件過程中很容易發(fā)生oom。
2.故障模擬
今天我們要模擬的故障是一種常見的內(nèi)存泄漏从诲。源代碼如下:
package com.brianxia.error;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
public class MemoryLeak {
//存儲(chǔ)內(nèi)存泄漏的數(shù)據(jù)
public static Map<String, String> data = new HashMap<>();
public static void addData(String key) {
data.put(key, "");
}
public static void deleteData(String key) {
data.remove(key, "");
}
public static void throwEx() throws Exception {
throw new RuntimeException("leak");
}
//內(nèi)存泄漏代碼左痢,添加數(shù)據(jù)之后拋出異常,從而無法執(zhí)行刪除數(shù)據(jù)的代碼系洛,造成內(nèi)存泄漏的現(xiàn)象
public static void leak() throws Exception {
Random random = new Random();
String key = String.valueOf(random.nextInt());
addData(key);
throwEx();
deleteData(key);
}
public static void main(String[] args) {
while (true) {
try {
leak();
Thread.sleep(100);
} catch (Exception e) {
if (!(e instanceof RuntimeException))
e.printStackTrace();
}
}
}
}
這是一段最簡單的內(nèi)存泄漏代碼俊性,本意是在leak函數(shù)中,通過add和delete描扯,回收掉添加到hashmap中的數(shù)據(jù)定页,但是在執(zhí)行add和delete的中間發(fā)生了異常,所以代碼路經(jīng)直接會(huì)跑到main函數(shù)中的異常捕獲中绽诚,從而hashmap中的數(shù)據(jù)永遠(yuǎn)不會(huì)被回收了典徊。
3.故障分析
關(guān)于如何識(shí)別是否有內(nèi)存泄漏,不在本文討論范疇內(nèi)恩够,后續(xù)會(huì)更新相應(yīng)的文章卒落。首先我們用上一次提到的jcmd來生成dump文件。
g>jcmd 6172 GC.heap_dump d:\dump_leak
6172:
Heap dump file created
打開visualvm蜂桶,選擇load剛才生成的文件儡毕。
選擇Objects,查看下目前JVM中的對象狀態(tài)扑媚。
從上圖中可以看到腰湾,我們的代碼產(chǎn)生了非常多的對象,其中主要是char[]疆股、Hashmap的Node檐盟、String。那么這個(gè)時(shí)候我們就需要根據(jù)我們的項(xiàng)目具體進(jìn)行分析了押桃,首先我們的代碼中存在HashMap, key的數(shù)據(jù)類型是String导犹,而String的底層實(shí)現(xiàn)又是char[]唱凯,這三個(gè)的數(shù)量可以看到基本一致。
從上面的信息我們可以推斷出谎痢,我們代碼實(shí)現(xiàn)中存在內(nèi)存泄漏(當(dāng)然也有可能是未進(jìn)行GC磕昼,因?yàn)槭茄菔居美覀兛梢栽趘isualvm上手動(dòng)執(zhí)行GC)节猿。
點(diǎn)開詳細(xì)的Object列表票从,可以看到相關(guān)的reference信息漫雕。從上圖可以知道,我們的Hashmap Node主要是在MemoryLeak類中的static變量data中被引用到峰鄙,無法得到釋放浸间。
之后就需要各位小伙伴去查驗(yàn)整個(gè)變量的生命周期,確認(rèn)為何資源沒有被回收吟榴。
4.總結(jié)
以上就是基本的生產(chǎn)下處理問題的流程魁蒜,需要注意的是,生產(chǎn)上可以打開
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=${目錄}
這兩個(gè)參數(shù)吩翻,讓發(fā)生OOM的時(shí)候自動(dòng)生成dump用于后續(xù)分析兜看。當(dāng)然最好的情況是在測試環(huán)境中能夠通過分析heap信息發(fā)現(xiàn)問題,而不是到生產(chǎn)上再去解決狭瞎。
下一章中细移,我會(huì)講解JVM內(nèi)存的基本原理及如何分析內(nèi)存狀態(tài)。
作者:BrianXia
轉(zhuǎn)載請注明 http://www.reibang.com/p/065d12dd3e44