1.背景
上週在生產環境應用啟動時,發生應用頻頻發生死鎖的現象。原因是因為 spring ioc 容器還未初始化完成,就有工作執行緒呼叫 context.getbean() 來獲取容器裡的物件。具體產生死鎖的原因條件有:
1. 應用啟動的時候 main 執行緒進行 spring 容器初始化。
2. 容器初始化的過程中有工作執行緒也起來了並開始工作。
3. 工作執行緒**裡顯式呼叫 spring ioc 容器的 context.getbean(string beanname) 。
4. 工作執行緒顯式獲取的 bean 未例項化,且裡存在直接或者間接的註解注入方式的情況。
以上情況都符合,那工作執行緒和 main 執行緒可能發生死鎖。
2.具體原因分析
spring ioc 容器組合裡有兩個重要的 map :
/** map of bean definition objects, keyed by bean name */
privatefinalmap
beandefinitionmap
= collectionfactory.createconcurrentmapifpossible (16);
//bean definition 是 spring 容器裡描述 bean 物件的元資料( bean 的建立等就是基於此來建立)。 spring 容器初始化例項之前需要先把配置檔案的 bean 定義都轉化成內部的統一描述物件 beandefinition 。該 beandefinitionmap 用於儲存這些資料。
/** cache of singleton objects: bean name
-->
bean instance */
privatefinalmap
singletonobjects
= collectionfactory.createconcurrentmapifpossible (16);
//spring 容器用於 cache 住 spring 容器初始化的單例物件
以上兩個物件為了保證資料的一致性,在操作的時候很多時候會進行加鎖。如以下兩個過程。
過程一: spring容器初始化
spring 容器初始化的時候會例項化所有單例物件( preinstantiatesingletons ),這個過程中會對上面兩個物件加鎖,以防止併發。先對 beandefinitionmap 加鎖,防止元資料被修改,然後在每次例項化單例物件的時候對 singletonobjects 加鎖,防止併發修改。
過程二:根據 spring容器獲取乙個單例物件。
呼叫 spring 容器的 context.getbean ( beanname ),如果該 bean 是單例且還未例項化,這個時候就需要進行例項化,如果該 bean 直接或間接存在註解方式的 bean 注入的時候,過程中也會對以上兩個物件進行加鎖防止併發。先對 singleobjects 加鎖,從改 map 裡找是否有存在 beanname 的物件,沒有的話在建立該 bean 的過程中會對 beandefinitionmap 加鎖。
可以看出以上過程一和過程二對兩個物件的鎖順序是不一致的,所以併發執行就可能會發生死鎖。
在本機寫了乙個簡單的實驗,死鎖的執行緒棧資訊可以證明這一點,具體如下:
**十分簡單,如下:
package com.alibaba.test;
public class deadlocktest
public void getbean()
/*** @param args
*/public static void main(string args)
//get bean 工作執行緒
class getbean implements runnable catch (interruptedexception e)
// todo auto-generated method stub}}
}
其中mybean定義如下(一定要用註解的注入方式,才會有可能發生死鎖!):
package com.alibaba.test;
import org.springframework.beans.factory.annotation.autowired;
public class mybean
public void setotherbean(otherbean otherbean)
public void sayhello()
}
mybean.xml:
<?xml version="1.0" encoding="gb2312"?>以上**經過除錯控制,即會發生思索,控制如下:
1.main執行緒在defaultlistablebeanfactory.preinstantiatesingletons 方法的
synchronized (this.beandefinitionmap) 裡加個斷點
2.getbean工作執行緒在defaultsingletonbeanregistry.getsingleton(string beanname, objectfactory singletonfactory)方法的
synchronized (this.singletonobjects) 裡加個斷點。
3.等1,2斷點都進去之後,再觸發繼續執行,就會發生死鎖。
結論也許可以算是 spring 的 bug ,也許可以算我們使用不當。
總之,有兩點需要注意:
1. 盡量避免顯式呼叫 ioc 容器,注入工作由容器自己來完成。
2. 盡量在容器初始化完,開始對外服務。
Minor GC ,Full GC 觸發條件
首先澄清一點,關於full gc和major gc,個人理解是同乙個東西。minor gc觸發條件 當eden區滿時,觸發minor gc。full gc觸發條件 1 呼叫system.gc時,系統建議執行full gc,但是不必然執行 2 老年代空間不足 3 方法區空間不足 4 通過minor g...
Minor GC ,Full GC 觸發條件
minor gc full gc 觸發條件 minor gc觸發條件 當eden區滿時,觸發minor gc。full gc觸發條件 1 呼叫system.gc時,系統建議執行full gc,但是不必然執行 2 老年代空間不足 3 方法去空間不足 4 通過minor gc後進入老年代的平均大小大於老...
高併發條件下的快取穿透問題
1 快取穿透 快取穿透是指查詢乙個一定不存在的資料,由於快取是不命中時被動寫的,並且出於容錯考慮,如果從儲存層查不到資料則不寫入快取,這將導致這個不存在的資料每次請求都要到儲存層去查詢,失去了快取的意義。在流量大時,可能db就掛掉了,要是有人利用不存在的key頻繁攻擊我們的應用,這就是漏洞。key不...