内存溢出_JVM|03内存溢出实战
實(shí)戰(zhàn) 內(nèi)存溢出定位與分析
環(huán)境搭建
/** * 模擬測試插入一百萬條字符串[image.png](https://img.hacpai.com/file/2019/08/image-dd10de62.png) str += UUID.randomUUID().toString(); } list.add(str); } System.out.println("ok"); }}結(jié)果:
java.lang.OutOfMemoryError: Java heap spaceDumping heap to java_pid10272.hprof ...Heap dump file created [8323049 bytes in 0.028 secs]Exception in thread "main" java.lang.OutOfMemoryError: Java heap spaceat java.util.Arrays.copyOf(Arrays.java:3332)at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448)at java.lang.StringBuilder.append(StringBuilder.java:136)at cn.jeff.test.Test01.main(Test01.java:18)Process finished with exit code 1參數(shù)中設(shè)置了輸出dump文件,默認(rèn)輸出在項(xiàng)目根目錄下。
問題分析(基于MAT):
可以看出87.93%的內(nèi)存被Object[]數(shù)組占用了,通常情況下不會出現(xiàn)這么高的占用
再去看詳細(xì)的數(shù)據(jù)情況:
可以看出在Object[]數(shù)組中裝滿了剛才生產(chǎn)的UUID,導(dǎo)致了最后的系統(tǒng)內(nèi)存溢出問題。
分析線程執(zhí)行情況:jstack
線程知識回顧
線程的六種狀態(tài)
初始態(tài)(NEW)
創(chuàng)建一個Thread對象,但還未調(diào)用start()啟動線程時,線程處于初始態(tài)。
運(yùn)行態(tài)(RUNNABLE),在Java中,運(yùn)行態(tài)包括 就緒態(tài) 和 運(yùn)行態(tài)。
2.1 就緒態(tài) :
該狀態(tài)下的線程已經(jīng)獲得執(zhí)行所需的所有資源,只要CPU分配執(zhí)行權(quán)就能運(yùn)
行。
所有就緒態(tài)的線程存放在就緒隊(duì)列中。
2.2 運(yùn)行態(tài) :
獲得CPU執(zhí)行權(quán),正在執(zhí)行的線程。
由于一個CPU同一時刻只能執(zhí)行一條線程,因此每個CPU每個時刻只有一條
運(yùn)行態(tài)的線程。
阻塞態(tài)(BLOCKED)
當(dāng)一條正在執(zhí)行的線程請求某一資源失敗時,就會進(jìn)入阻塞態(tài)。
而在Java中,阻塞態(tài)專指請求鎖失敗時進(jìn)入的狀態(tài)。
由一個阻塞隊(duì)列存放所有阻塞態(tài)的線程。
處于阻塞態(tài)的線程會不斷請求資源,一旦請求成功,就會進(jìn)入就緒隊(duì)列,等待執(zhí)
行。
等待態(tài)(WAITING)
當(dāng)前線程中調(diào)用wait、join、park函數(shù)時,當(dāng)前線程就會進(jìn)入等待態(tài)。
也有一個等待隊(duì)列存放所有等待態(tài)的線程。
線程處于等待態(tài)表示它需要等待其他線程的指示才能繼續(xù)運(yùn)行。進(jìn)入等待態(tài)的線程會釋放CPU執(zhí)行權(quán),并釋放資源(如:鎖)
超時等待態(tài)(TIMED_WAITING)
當(dāng)運(yùn)行中的線程調(diào)用sleep(time)、wait、join、parkNanos、parkUntil時,就
會進(jìn)入該狀態(tài);
它和等待態(tài)一樣,并不是因?yàn)檎埱蟛坏劫Y源,而是主動進(jìn)入,并且進(jìn)入后需要其
他線程喚醒;
進(jìn)入該狀態(tài)后釋放CPU執(zhí)行權(quán) 和 占有的資源。
與等待態(tài)的區(qū)別:到了超時時間后自動進(jìn)入阻塞隊(duì)列,開始競爭鎖。
終止態(tài)(TERMINATED)
線程執(zhí)行結(jié)束后的狀態(tài)。
實(shí)戰(zhàn):死鎖問題
構(gòu)建死鎖環(huán)境
public class TestDeadLock { // 定義兩個鎖 private static Object obj1 = new Object(); private static Object obj2 = new Object(); public static void main(String[] args) {// 創(chuàng)建初始態(tài)線程 Thread thread1 = new Thread(new Thread1()); Thread thread2 = new Thread(new Thread2());// 就緒態(tài) thread1.start(); thread2.start(); } // 第一個線程 private static class Thread1 implements Runnable { @Override public void run() { synchronized (obj1) {// 此時得到了obj1這個鎖 System.out.println("Thread1得到了obj1這把鎖!"); try {// 停下來休息兩秒 為了讓Thread2得到obj2這把鎖 造成死鎖 sleep方法是不會釋放鎖的 Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (obj2) { System.out.println("Thread1得到了obj2這把鎖!"); } } } } // 第二個線程 private static class Thread2 implements Runnable { @Override public void run() {// 得到obj2這把鎖 synchronized (obj2) { System.out.println("Thread2得到了obj2這把鎖!"); try {// 為了讓obj1被先得到 Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }// 得到obj1 synchronized (obj1) { System.out.println("Thread2得到了obj1這把鎖!"); } } } }}結(jié)果
[root@hadoop101 jvm]# javac TestDeadLock.java[root@hadoop101 jvm]# java TestDeadLockThread1得到了obj1這把鎖!Thread2得到了obj2這把鎖!# 程序卡在這里保持程序的運(yùn)行狀態(tài),另外創(chuàng)建一個xshell命令行窗口
# 通過jps找到運(yùn)行的TestDeadLock程序的端口[root@hadoop101 ~]# jps2956 TestDeadLock3038 Jps1967 Bootstrap# 指令格式:jstack 端口號[root@hadoop101 ~]# jstack 29562019-08-16 02:43:12Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.111-b14 mixed mode):"Attach Listener" #11 daemon prio=9 os_prio=0 tid=0x00007ffa54001000 nid=0xbf4 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE"DestroyJavaVM" #10 prio=5 os_prio=0 tid=0x00007ffa7c008800 nid=0xb8d waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE"Thread-1" #9 prio=5 os_prio=0 tid=0x00007ffa7c0ca800 nid=0xb97 waiting for monitor entry [0x00007ffa6c51d000] java.lang.Thread.State: BLOCKED (on object monitor)at TestDeadLock$Thread2.run(TestDeadLock.java:52)- waiting to lock <0x00000000e345be78> (a java.lang.Object)- locked <0x00000000e345be88> (a java.lang.Object)at java.lang.Thread.run(Thread.java:745)"Thread-0" #8 prio=5 os_prio=0 tid=0x00007ffa7c0c9000 nid=0xb96 waiting for monitor entry [0x00007ffa6c61e000] java.lang.Thread.State: BLOCKED (on object monitor)at TestDeadLock$Thread1.run(TestDeadLock.java:31)- waiting to lock <0x00000000e345be88> (a java.lang.Object)- locked <0x00000000e345be78> (a java.lang.Object)at java.lang.Thread.run(Thread.java:745)"Service Thread" #7 daemon prio=9 os_prio=0 tid=0x00007ffa7c0b3000 nid=0xb94 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE"C1 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007ffa7c0b0000 nid=0xb93 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007ffa7c0ad800 nid=0xb92 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE"Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007ffa7c0ac000 nid=0xb91 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007ffa7c079000 nid=0xb90 in Object.wait() [0x00007ffa6cc24000] java.lang.Thread.State: WAITING (on object monitor)at java.lang.Object.wait(Native Method)- waiting on <0x00000000e3408e98> (a java.lang.ref.ReferenceQueue$Lock)at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)- locked <0x00000000e3408e98> (a java.lang.ref.ReferenceQueue$Lock)at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007ffa7c074800 nid=0xb8f in Object.wait() [0x00007ffa6cd25000] java.lang.Thread.State: WAITING (on object monitor)at java.lang.Object.wait(Native Method)- waiting on <0x00000000e3406b40> (a java.lang.ref.Reference$Lock)at java.lang.Object.wait(Object.java:502)at java.lang.ref.Reference.tryHandlePending(Reference.java:191)- locked <0x00000000e3406b40> (a java.lang.ref.Reference$Lock)at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)"VM Thread" os_prio=0 tid=0x00007ffa7c06d000 nid=0xb8e runnable"VM Periodic Task Thread" os_prio=0 tid=0x00007ffa7c0b6000 nid=0xb95 waiting on conditionJNI global references: 6Found one Java-level deadlock:============================="Thread-1": waiting to lock monitor 0x00007ffa600062c8 (object 0x00000000e345be78, a java.lang.Object), which is held by "Thread-0""Thread-0": waiting to lock monitor 0x00007ffa60004e28 (object 0x00000000e345be88, a java.lang.Object), which is held by "Thread-1"Java stack information for the threads listed above:==================================================="Thread-1":at TestDeadLock$Thread2.run(TestDeadLock.java:52)- waiting to lock <0x00000000e345be78> (a java.lang.Object)- locked <0x00000000e345be88> (a java.lang.Object)at java.lang.Thread.run(Thread.java:745)"Thread-0":at TestDeadLock$Thread1.run(TestDeadLock.java:31)- waiting to lock <0x00000000e345be88> (a java.lang.Object)- locked <0x00000000e345be78> (a java.lang.Object)at java.lang.Thread.run(Thread.java:745)# 系統(tǒng)提示發(fā)現(xiàn)一個死鎖Found 1 deadlock.死鎖分析:
"Thread-1" #9 prio=5 os_prio=0 tid=0x00007ffa7c0ca800 nid=0xb97 waiting for monitor entry [0x00007ffa6c51d000] java.lang.Thread.State: BLOCKED (on object monitor)at TestDeadLock$Thread2.run(TestDeadLock.java:52)- waiting to lock <0x00000000e345be78> (a java.lang.Object)- locked <0x00000000e345be88> (a java.lang.Object)at java.lang.Thread.run(Thread.java:745)"Thread-0" #8 prio=5 os_prio=0 tid=0x00007ffa7c0c9000 nid=0xb96 waiting for monitor entry [0x00007ffa6c61e000] java.lang.Thread.State: BLOCKED (on object monitor)at TestDeadLock$Thread1.run(TestDeadLock.java:31)- waiting to lock <0x00000000e345be88> (a java.lang.Object)- locked <0x00000000e345be78> (a java.lang.Object)at java.lang.Thread.run(Thread.java:745)可以看出:
Thread-1:正在手握著0x00000000e345be88這個鎖,等待著0x00000000e345be78的獲取;
Thread-0:正在手握著0x00000000e345be78這個鎖,等待著0x00000000e345be88的獲取;
VisualVM工具的使用
簡介
VisualVM,能夠監(jiān)控線程,內(nèi)存情況,查看方法的CPU時間和內(nèi)存中的對 象,已被GC的對象,反向查看分配的堆棧(如100個String對象分別由哪幾個對象分配出來的)。
VisualVM使用簡單,幾乎0配置,功能還是比較豐富的,幾乎囊括了其它JDK自帶命令的所有功能。
1、內(nèi)存信息
2、線程信息
3、Dump堆(本地進(jìn)程)
4、Dump線程(本地進(jìn)程)
5、打開堆Dump。堆Dump可以用jmap來生成。
6、打開線程Dump
7、生成應(yīng)用快照(包含內(nèi)存信息、線程信息等等)
8、性能分析。
9、CPU分析(各個方法調(diào)用時間,檢查哪些方法耗時多)
10、內(nèi)存分析(各類對象占用的內(nèi)存,檢查哪些類占用內(nèi)存多
使用方法
啟動
VisualVM在jdk的安裝目錄的bin下面有jvisualvm.exe,打開便可使用。
與50位技術(shù)專家面對面20年技術(shù)見證,附贈技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的内存溢出_JVM|03内存溢出实战的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 导出excel加粗_Python办公自动
- 下一篇: 根目录_Linux Shell从入门到删