摘要:?jiǎn)栴}分析之死鎖產(chǎn)生死鎖必須同時(shí)滿足以下四個(gè)條件互斥條件一段時(shí)間內(nèi)某資源只能被一個(gè)線程進(jìn)程占有,若有其他請(qǐng)求線程只能等待。問(wèn)題分析之內(nèi)存泄露內(nèi)存溢出堆內(nèi)存溢出內(nèi)存泄露指的是申請(qǐng)內(nèi)存后無(wú)法釋放該內(nèi)存。
問(wèn)題分析之死鎖
產(chǎn)生死鎖必須同時(shí)滿足以下四個(gè)條件:
互斥條件:一段時(shí)間內(nèi)某資源只能被一個(gè)線程(進(jìn)程)占有,若有其他請(qǐng)求線程只能等待。
不剝奪條件:一個(gè)線程占用某資源后只能該線程自己釋放資源,不能被其他線程奪走。
請(qǐng)求和保持條件:一個(gè)線程去申請(qǐng)另外一個(gè)資源的時(shí)候,繼續(xù)占有已分配的資源。
循環(huán)等待條件:存在一個(gè)處于等待狀態(tài)的線程集合{p1,...,pi,..},pi等待的資源被p(i+1)占有。
簡(jiǎn)單點(diǎn)說(shuō),對(duì)于兩個(gè)線程A,B而言,先有線程A占有鎖X,線程B占有鎖Y,然后A繼續(xù)申請(qǐng)鎖Y,B繼續(xù)申請(qǐng)鎖X,但由于此時(shí)鎖Y已經(jīng)被B占有,A只能等待B釋放鎖Y,同理B也在等待A釋放鎖X。此時(shí)形成了一個(gè)線程分別等待對(duì)方釋放鎖的狀況,即產(chǎn)生了死鎖。
public class DeadLock { private static Lock lockA = new ReentrantLock(); private static Lock lockB = new ReentrantLock(); /*private static Object monitor1 = new Object(); private static Object monitor2 = new Object();*/ public static void main(String[] args) { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } new ThreadA().start(); new ThreadB().start(); } static class ThreadA extends Thread{ @Override public void run() { lockA.lock(); try { Thread.sleep(2000); lockB.lock(); System.out.println("in lockB"); lockB.unlock(); } catch (Exception e) { // TODO: handle exception }finally{ lockA.unlock(); } /* synchronized (monitor1) { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (monitor2) { System.out.println("in monitor2"); } }*/ } } static class ThreadB extends Thread{ @Override public void run() { lockB.lock(); try{ Thread.sleep(4000); lockA.lock(); System.out.println("in lockA"); lockA.unlock(); }catch(Exception e){ e.printStackTrace(); }finally{ lockB.unlock(); } /* synchronized (monitor2) { try { Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized(monitor1){ System.out.println("in monitor2"); } } */ } } }
上面代碼是一個(gè)簡(jiǎn)單的例子,產(chǎn)生死鎖一般可以用jstack命令生成線程快照來(lái)分析,當(dāng)然更好用的有jdk自帶的visualVM圖形化工具。在java_home目錄下的bin文件夾里面,可以找到j(luò)visualVM。在linux下可以使用命令行:
cd $JAVA_HOME/bin
./jvisualvm&
當(dāng)然JAVA_HOME一般都export到PATH下了,可以直接命令行輸入
jvisualvm&
在visualVM中進(jìn)入對(duì)應(yīng)的進(jìn)程,可以看到visualVM直接幫助我們檢測(cè)到了死鎖:
點(diǎn)擊線程dump按鈕,查看dump堆文件:
由于這里的死鎖程序使用的Lock鎖,可以看到兩個(gè)線程Thread-0,Thread-1的狀態(tài)為WAITING(如果使用上面程序注釋掉的synchronized鎖,線程狀態(tài)為阻塞)。Thread-1已擁有鎖的id為<...71bc8>,等待鎖id為<...73008>,相反Thread-0擁有鎖<...73008>,正在等待鎖<...71bc8>。
內(nèi)存泄露memory leak:指的是申請(qǐng)內(nèi)存后無(wú)法釋放該內(nèi)存。在java當(dāng)中指的是存在無(wú)用,而且是可達(dá)的(導(dǎo)致jvm無(wú)法回收)的對(duì)象。
內(nèi)存溢出out of memory:指的是申請(qǐng)內(nèi)存時(shí),已沒(méi)有足夠的內(nèi)存空間供使用。
內(nèi)存泄露如果大量的堆積,消耗足夠多的內(nèi)存,最后會(huì)產(chǎn)生內(nèi)存溢出。
下面是一個(gè)內(nèi)存泄露最終導(dǎo)致內(nèi)存溢出的例子:
public class MemoryLeak { public static void main(String[] args) { sleep(9000); Vector v = new Vector(); long count = 0; while (true) { Object o = new Object(); v.add(o); o = null; count++; if (count % 100 == 0) { System.out.println("vector size: " + v.size()); long freeMem = Runtime.getRuntime().freeMemory() / (1024 * 1024); System.out.println("freeMemory is " + freeMem + "M in count->" + count); } } } private static void sleep(long millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { e.printStackTrace(); } } }
我們加上jvm啟動(dòng)參數(shù),將最小和最大堆大小均設(shè)為20M:
-Xms20m -Xmx20m
最后得到溢出異常:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.Vector.grow(Vector.java:266)
at java.util.Vector.ensureCapacityHelper(Vector.java:246)
at java.util.Vector.add(Vector.java:782)
at com.ethfoo.jvm.MemoryLeak.main(MemoryLeak.java:17)
代碼當(dāng)中我們不停的往Vector里面加Object對(duì)象,并且每個(gè)對(duì)象的引用o置為null。我們假設(shè)這些Object已是無(wú)用對(duì)象,雖然我們將o置為null,但其實(shí)Vector里面仍然保存每個(gè)Object對(duì)象的引用,所以O(shè)bject對(duì)jvm來(lái)說(shuō)是可達(dá)的,jvm無(wú)法對(duì)其進(jìn)行回收。
2. 方法區(qū)內(nèi)存溢出(outOfMemoryError:permgem space)在jvm規(guī)范中,方法區(qū)主要存放的是類信息、常量、靜態(tài)變量等。
所以如果程序加載的類過(guò)多,或者使用反射、gclib等這種動(dòng)態(tài)代理生成類的技術(shù),就可能導(dǎo)致該區(qū)發(fā)生內(nèi)存溢出,一般該區(qū)發(fā)生內(nèi)存溢出時(shí)的錯(cuò)誤信息為:
outOfMemoryError:permgem space
可以使用jvm參數(shù)調(diào)整方法區(qū)的大小分配:
3. 線程棧溢出(StackOverflowError)-XX:PermSize -XX:MaxPermSize
一般線程棧溢出是由于遞歸太深或方法調(diào)用層級(jí)過(guò)多導(dǎo)致的。
可以使用以下參數(shù)來(lái)調(diào)整棧大小的分配,線程棧越大遞歸調(diào)用的深度越大,但同時(shí)由于總的內(nèi)存大小限制,會(huì)使總體能夠啟動(dòng)的線程數(shù)目減小。
4. 直接內(nèi)存溢出(OutOfMemoryError: Direct buffer memory)-Xss
試運(yùn)行以下代碼,直接分配大量堆外內(nèi)存:
public static void main(String args[]){ for(int i=0; i<3024; i++){ ByteBuffer.allocateDirect(1024*1024*1024); System.out.println(i); //System.gc(); } }
最后導(dǎo)致結(jié)果:
Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
at java.nio.Bits.reserveMemory(Bits.java:658)
at java.nio.DirectByteBuffer.(DirectByteBuffer.java:123)
at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)
at com.ethfoo.jvm.OutOfMemory.directMemeory(OutOfMemory.java:28)
at com.ethfoo.jvm.OutOfMemory.main(OutOfMemory.java:10)
需要注意的是,直接內(nèi)存是無(wú)法觸發(fā)jvm的內(nèi)存回收機(jī)制的,直接內(nèi)存可以被垃圾收集器回收,但是只能是由堆中內(nèi)存觸發(fā)gc,同時(shí)順便對(duì)直接內(nèi)存進(jìn)行垃圾收集清理。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/64823.html
摘要:在之前,它是一個(gè)備受爭(zhēng)議的關(guān)鍵字,因?yàn)樵诔绦蛑惺褂盟占骼斫夂驮矸治龊?jiǎn)稱,是后提供的面向大內(nèi)存區(qū)數(shù)到數(shù)多核系統(tǒng)的收集器,能夠?qū)崿F(xiàn)軟停頓目標(biāo)收集并且具有高吞吐量具有更可預(yù)測(cè)的停頓時(shí)間。 35 個(gè) Java 代碼性能優(yōu)化總結(jié) 優(yōu)化代碼可以減小代碼的體積,提高代碼運(yùn)行的效率。 從 JVM 內(nèi)存模型談線程安全 小白哥帶你打通任督二脈 Java使用讀寫(xiě)鎖替代同步鎖 應(yīng)用情景 前一陣有個(gè)做...
摘要:然而偶爾的一次聚會(huì),你聽(tīng)說(shuō)和自己一起出道的同學(xué)早已經(jīng)年薪萬(wàn),而自己卻囊中羞澀。這個(gè)時(shí)候,你可能會(huì)懷疑自己的能力,也痛恨為什么當(dāng)初自己沒(méi)有好好復(fù)習(xí)。 作為一個(gè) Java 程序員,我們深知水平的深淺決定你的收入高低,月工資下到七八千,上到十幾萬(wàn)都是很正常的事情。許多人的現(xiàn)狀是平時(shí)總是陷在業(yè)務(wù)開(kāi)發(fā)...
摘要:接私活對(duì)程序員這個(gè)圈子來(lái)說(shuō)是一個(gè)既公開(kāi)又隱私的話題,不說(shuō)全部,應(yīng)該大多數(shù)程序員都有過(guò)想要接私活的想法,當(dāng)然,也有部分得道成仙的不主張接私活。 接私活 對(duì)程序員這個(gè)圈子來(lái)說(shuō)是一個(gè)既公開(kāi)又隱私的話題,不說(shuō)全部,應(yīng)該大多數(shù)程序員都有過(guò)想要接私活的想法,當(dāng)然,也有部分得道成仙的不主張接私活。但是很少...
摘要:可現(xiàn)在五年過(guò)去了,他想跳槽卻鮮有人問(wèn)津。最可氣的是比他晚一年畢業(yè)的學(xué)弟,勤勤懇懇在一家中型互聯(lián)網(wǎng)企業(yè)干了年,現(xiàn)在已經(jīng)跳槽到了阿里,月薪是我這個(gè)同學(xué)的倍。 我有個(gè)同學(xué)大學(xué)畢業(yè),因?yàn)閰s少工作經(jīng)驗(yàn),又不愿意去正經(jīng)的互聯(lián)網(wǎng)企業(yè)做實(shí)習(xí)生,他嫌工資太低,于是進(jìn)了家外包公司,那時(shí)候感覺(jué)待遇還可以。可現(xiàn)在五...
摘要:同時(shí)也會(huì)關(guān)注市場(chǎng)上同崗位薪資,以便對(duì)企業(yè)內(nèi)部薪資結(jié)構(gòu)做出相應(yīng)調(diào)整。一般來(lái)說(shuō),相同崗位和職責(zé)的員工,薪資低于市場(chǎng)不超過(guò),都屬于合理范疇,因?yàn)橐粋€(gè)員工不會(huì)為了的薪酬而跳槽。同時(shí),還能激勵(lì)員工自我提升,以獲得相應(yīng)技能市場(chǎng)所給予的報(bào)酬。 各位職場(chǎng)人都聽(tīng)說(shuō)過(guò)薪資倒掛這詞兒吧,這個(gè)情況在行業(yè)內(nèi)早就不是什...
閱讀 3166·2021-11-11 16:54
閱讀 2344·2021-09-04 16:48
閱讀 3252·2019-08-29 16:08
閱讀 671·2019-08-29 15:13
閱讀 1379·2019-08-29 15:09
閱讀 2700·2019-08-29 12:45
閱讀 1958·2019-08-29 12:12
閱讀 479·2019-08-26 18:27