Java垃圾回收機制(GC)
1.GC機制作用
1.1 JVM自動檢測和釋放不再使用的對象內(nèi)存
1.2 Java 運行時JVM會執(zhí)行 GC,不再需要顯式釋放對象
例:Object.finallize()、 Windows.dispose()、 System.gc()
2.Java堆3代分布
關(guān)于Java堆3代分布情況,可通過命令:jmap –heap pid 查看
3.GC分類
3.1 Young GC(Minor GC):收集生命周期短的區(qū)域(Young)
(1) 清空Eden+from survivor中所有no ref的對象占用的內(nèi)存
(2) 將Eden+from survivor中所有存活的對象copy到to survivor中
(3) 一些對象將晉升到old中: to survivor放不下的或存活次數(shù)超過turning threshold中的
3.2 Full GC(Major GC):收集生命周期短的區(qū)域(Young)和生命周期比較長的區(qū)域(Old),對整個堆進行垃圾收集,有時也會回收持久區(qū)(Perm)
(1) 清空heap中no ref的對象
(2) 清空permgen中已經(jīng)被卸載的class信息
4.GC過程
(1) 新生成的對象在Eden區(qū)完成內(nèi)存分配
(2) 當Eden區(qū)滿,再創(chuàng)建對象,會因為申請不到空間觸發(fā)YGC,進行young(eden+1survivor)區(qū)的垃圾回收(為什么是eden+1survivor:兩個survivor中始終有一個survivor是空的,空的那個被標記成To Survivor)
(3) YGC時,Eden不能被回收的對象被放入到空的survivor(也就是放到To Survivor,此時Eden被清空),另一個survivor(From Survivor)里不能被GC回收的對象也會被放入To Survivor,始終保證一個survivor是空的(YGC完成之后,To Survivor 和 From Survivor的標記互換)
(4) YGC結(jié)束后,若存放對象的survivor滿,則這些對象被copy到old區(qū),或者survivor區(qū)沒有滿,但是有些對象已經(jīng)足夠Old(超過XX:MaxTenuringThreshold),也被放入Old區(qū)
(5) 當Old區(qū)被放滿的之后,進行完整的垃圾回收,即 FGC
(6) FGC后,若Survivor及old區(qū)仍然無法存放從Eden復制過來的部分對象,導致JVM無法在Eden區(qū)為新對象創(chuàng)建內(nèi)存區(qū)域,則出現(xiàn)OOM錯誤
Java應用內(nèi)存問題分析方法
1.Java內(nèi)存劃分
可粗略劃分三類:
1.1 堆內(nèi)存
存放由 new 創(chuàng)建的對象和數(shù)組,在堆中分配的內(nèi)存,由 Java 虛擬機的自動垃圾回收器來管理
1.2 棧內(nèi)存
在函數(shù)中定義的一些基本類型的變量和對象的引用變量都是在函數(shù)的棧內(nèi)存中分配(更準確地說是保存了引用的堆內(nèi)存空間的地址,java中的“指針”)
1.3 永久保存區(qū)、方法區(qū)(Permanent Generation)
用于存儲已被虛擬機加載的類信息、常量、靜態(tài)變量等
2.Java常見的內(nèi)存問題表現(xiàn)形式:
2.1 OutOfMemory:內(nèi)存溢出
2.2 Memory Leak:內(nèi)存泄露
二者共同點:
(1) 通常最終的狀態(tài)就會導致OOM錯誤
(2) 在Java堆或本地內(nèi)存中都可能發(fā)生
二者不同點:
(1) ML是已經(jīng)分配好的內(nèi)存或?qū)ο?,當不再需要,沒有得到釋放 而OOM則是沒有足夠的空間來供jvm分配新的內(nèi)存塊
(2) ML的內(nèi)存曲線總體上是一條斜向上的曲線而OOM不是,反之未必
3.內(nèi)存溢出類型:
虛擬機棧溢出、本地方法棧溢出、方法區(qū)溢出、堆溢出、運行時常量池溢出
異常類型:
(1) java.lang.OutOfMemoryError: Java heap space
堆內(nèi)存溢出
優(yōu)化:通過-Xmn(最小值)–Xms(初始值) -Xmx(最大值)參數(shù)手動設(shè)置 Heap(堆)的大小。
(2) java.lang.OutOfMemoryError: PermGen space
PermGen Space溢出(方法區(qū)溢出、運行時常量池溢出)
優(yōu)化:通過MaxPermSize參數(shù)設(shè)置PermGen space大小。
(3) java.lang.StackOverflowError
棧溢出(虛擬機棧溢出、本地方法棧溢出)
優(yōu)化:通過Xss參數(shù)調(diào)整
Demo代碼 :
// Java 堆溢出 public static void main(String[] args) { List<OOMObject> list = new ArrayList<JavaHeapSpace.OOMObject>(); while (true) { list.add(new OOMObject()); } } static class OOMObject { }
// 虛擬機棧溢出 public static void main(String[] args) { // TODO Auto-generated method stub System.out.println(add()); } public static int add(){ return add(); }
// 方法區(qū)溢出 public static void main(String[] args) { while (true) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(OOMObject.class); enhancer.setUseCache(false); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { return proxy.invoke(obj, args); } }); enhancer.create(); } } static class OOMObject { }
// 運行時常量池溢出 public static void main(String[] args){ // TODO Auto-generated method stub List<String> list = new ArrayList<String>(); int i = 0; while (true ){ list.add(String. valueOf(i++).intern()); } }
// 內(nèi)存泄露模擬 public static void main(String[] args) { // TODO Auto-generated method stub List<int[]> list = new ArrayList<int[]>(); Runtime run = Runtime.getRuntime(); int i=1; while(true){ int[] arr = new int[1024]; list.add(arr); if(i++ % 1000 == 0 ){ System.out.print("最大堆內(nèi)存=" + run.maxMemory() / 1024 / 1024 + "M, "); System.out.print("已分配內(nèi)存=" + run.totalMemory() /1024 / 1024 + "M, "); System.out.print("剩余空間內(nèi)存=" + run.freeMemory() / 1024 / 1024 + "M, "); System.out.println("最大可用內(nèi)存=" + ( run.maxMemory() - run.totalMemory() + run.freeMemory() ) / 1024 / 1024 + "M"); sleep(1000); } } } public static void sleep(long time) { try { Thread.sleep(time); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
4.內(nèi)存泄露現(xiàn)象
heapspace:OutOfMemoryError
開發(fā)人員的分析、解決思路
內(nèi)存對象申請未釋放(未及時釋放)
線程問題
分別從堆dump和線程dump進行分析:
jmap -dump:format=b,file=heap.dump pid
jstack pid >> thread.dump
5.JAVA DUMP分析工具
IBM HeapAnalyzer:ha456.jar
IBM Thread and Monitor Dump Analyzer:jca457.jar
堆dump分析
占用內(nèi)存較多代碼塊
分析代碼快上下文
分析占用內(nèi)存的對象內(nèi)容
線程dump分析
活躍線程
阻塞線程
等待資源線程
Java應用CPU問題分析方法
1.程序響應慢,CPU高
(1) ThreadDump
jstack pid >> thread.dump
(2) 找到導致cpu高的線程 top -H -p pid
(3) pid 十進制轉(zhuǎn)十六進制
http://tool.oschina.net/hexconvert/
(4) 找到對應的線程UE打開 threaddump文件查找:按十六進制關(guān)鍵字找到對應的線程,把相關(guān)的方法找出來,可以精確到代碼的行號
2.程序響應慢,CPU不高
一般表現(xiàn)為thread struck在了i/o、db等
實例:
IO阻塞(程序表現(xiàn)為響應慢)
線程狀態(tài)為“in Object.wait()”,說明正在等待線程池可用資源,由于線程池滿導致新的IO請求處于排隊等待狀態(tài),且發(fā)生在:at com.iflytek.diange.data.provider.sendsong.impl.SendSongImpl.getSendSongInfosByUserId(SendSongImpl.java:92)行
3.程序無響應
死鎖(程序表現(xiàn)為無響應)
線程狀態(tài)為“waiting to lock”: 兩個線程各持有一個鎖,又在等待另一個鎖,故造成死鎖,且發(fā)生在DeadLockTest.java:39行
聯(lián)系客服