摘要:問題分析之死鎖產生死鎖必須同時滿足以下四個條件互斥條件一段時間內某資源只能被一個線程進程占有,若有其他請求線程只能等待。問題分析之內存泄露內存溢出堆內存溢出內存泄露指的是申請內存后無法釋放該內存。
問題分析之死鎖
產生死鎖必須同時滿足以下四個條件:
互斥條件:一段時間內某資源只能被一個線程(進程)占有,若有其他請求線程只能等待。
不剝奪條件:一個線程占用某資源后只能該線程自己釋放資源,不能被其他線程奪走。
請求和保持條件:一個線程去申請另外一個資源的時候,繼續占有已分配的資源。
循環等待條件:存在一個處于等待狀態的線程集合{p1,...,pi,..},pi等待的資源被p(i+1)占有。
簡單點說,對于兩個線程A,B而言,先有線程A占有鎖X,線程B占有鎖Y,然后A繼續申請鎖Y,B繼續申請鎖X,但由于此時鎖Y已經被B占有,A只能等待B釋放鎖Y,同理B也在等待A釋放鎖X。此時形成了一個線程分別等待對方釋放鎖的狀況,即產生了死鎖。
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"); } } */ } } }
上面代碼是一個簡單的例子,產生死鎖一般可以用jstack命令生成線程快照來分析,當然更好用的有jdk自帶的visualVM圖形化工具。在java_home目錄下的bin文件夾里面,可以找到jvisualVM。在linux下可以使用命令行:
cd $JAVA_HOME/bin
./jvisualvm&
當然JAVA_HOME一般都export到PATH下了,可以直接命令行輸入
jvisualvm&
在visualVM中進入對應的進程,可以看到visualVM直接幫助我們檢測到了死鎖:
點擊線程dump按鈕,查看dump堆文件:
由于這里的死鎖程序使用的Lock鎖,可以看到兩個線程Thread-0,Thread-1的狀態為WAITING(如果使用上面程序注釋掉的synchronized鎖,線程狀態為阻塞)。Thread-1已擁有鎖的id為<...71bc8>,等待鎖id為<...73008>,相反Thread-0擁有鎖<...73008>,正在等待鎖<...71bc8>。
內存泄露memory leak:指的是申請內存后無法釋放該內存。在java當中指的是存在無用,而且是可達的(導致jvm無法回收)的對象。
內存溢出out of memory:指的是申請內存時,已沒有足夠的內存空間供使用。
內存泄露如果大量的堆積,消耗足夠多的內存,最后會產生內存溢出。
下面是一個內存泄露最終導致內存溢出的例子:
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啟動參數,將最小和最大堆大小均設為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)
代碼當中我們不停的往Vector里面加Object對象,并且每個對象的引用o置為null。我們假設這些Object已是無用對象,雖然我們將o置為null,但其實Vector里面仍然保存每個Object對象的引用,所以Object對jvm來說是可達的,jvm無法對其進行回收。
2. 方法區內存溢出(outOfMemoryError:permgem space)在jvm規范中,方法區主要存放的是類信息、常量、靜態變量等。
所以如果程序加載的類過多,或者使用反射、gclib等這種動態代理生成類的技術,就可能導致該區發生內存溢出,一般該區發生內存溢出時的錯誤信息為:
outOfMemoryError:permgem space
可以使用jvm參數調整方法區的大小分配:
3. 線程棧溢出(StackOverflowError)-XX:PermSize -XX:MaxPermSize
一般線程棧溢出是由于遞歸太深或方法調用層級過多導致的。
可以使用以下參數來調整棧大小的分配,線程棧越大遞歸調用的深度越大,但同時由于總的內存大小限制,會使總體能夠啟動的線程數目減小。
4. 直接內存溢出(OutOfMemoryError: Direct buffer memory)-Xss
試運行以下代碼,直接分配大量堆外內存:
public static void main(String args[]){ for(int i=0; i<3024; i++){ ByteBuffer.allocateDirect(1024*1024*1024); System.out.println(i); //System.gc(); } }
最后導致結果:
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)
需要注意的是,直接內存是無法觸發jvm的內存回收機制的,直接內存可以被垃圾收集器回收,但是只能是由堆中內存觸發gc,同時順便對直接內存進行垃圾收集清理。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/64823.html
摘要:然而偶爾的一次聚會,你聽說和自己一起出道的同學早已經年薪萬,而自己卻囊中羞澀。這個時候,你可能會懷疑自己的能力,也痛恨為什么當初自己沒有好好復習。 作為一個 Java 程序員,我們深知水平的深淺決定你的收入高低,月工資下到七八千,上到十幾萬都是很正常的事情。許多人的現狀是平時總是陷在業務開發...
摘要:接私活對程序員這個圈子來說是一個既公開又隱私的話題,不說全部,應該大多數程序員都有過想要接私活的想法,當然,也有部分得道成仙的不主張接私活。 接私活 對程序員這個圈子來說是一個既公開又隱私的話題,不說全部,應該大多數程序員都有過想要接私活的想法,當然,也有部分得道成仙的不主張接私活。但是很少...
摘要:可現在五年過去了,他想跳槽卻鮮有人問津。最可氣的是比他晚一年畢業的學弟,勤勤懇懇在一家中型互聯網企業干了年,現在已經跳槽到了阿里,月薪是我這個同學的倍。 我有個同學大學畢業,因為卻少工作經驗,又不愿意去正經的互聯網企業做實習生,他嫌工資太低,于是進了家外包公司,那時候感覺待遇還可以??涩F在五...
摘要:同時也會關注市場上同崗位薪資,以便對企業內部薪資結構做出相應調整。一般來說,相同崗位和職責的員工,薪資低于市場不超過,都屬于合理范疇,因為一個員工不會為了的薪酬而跳槽。同時,還能激勵員工自我提升,以獲得相應技能市場所給予的報酬。 各位職場人都聽說過薪資倒掛這詞兒吧,這個情況在行業內早就不是什...
閱讀 3136·2021-11-11 16:54
閱讀 2291·2021-09-04 16:48
閱讀 3219·2019-08-29 16:08
閱讀 642·2019-08-29 15:13
閱讀 1344·2019-08-29 15:09
閱讀 2660·2019-08-29 12:45
閱讀 1926·2019-08-29 12:12
閱讀 444·2019-08-26 18:27