1. 程式人生 > >實戰Java虛擬機器筆記 7-8

實戰Java虛擬機器筆記 7-8

不變性可以提高多執行緒訪問的效能。因為物件不可變,因此對於所有執行緒都是隻讀的,多執行緒訪問時,即使不加同步也不會產生資料的不一致,故減小了系統開銷。

淺堆:表示一個物件結構所佔用的記憶體大小。 深堆:表示一個物件被GC回收後,可以真實釋放的記憶體大小。 實際大小:表示一個物件所能觸及的所有物件的淺堆大小之和。與深堆相比,似乎這個在日常開發中更為直觀和被人接受,但實際上,這個概念和垃圾回收無關。

支配樹體現了物件例項間的支配關係。在物件引用圖中,所有指向物件B的路徑都經過物件A,則認為物件A支配物件B。如果物件A是離物件B最近的一個支配物件,則認為物件A為物件B的直接支配者。支配樹是基於物件間的引用圖所建立的,它有以下基本性質: 1. 物件A的子樹(所有被物件A支配的物件集合)表示物件A的保留集(retained set),即深堆。 2. 如果物件A支配物件B,那麼物件A的直接支配者也支配物件B。 3. 支配樹的邊與物件引用圖的邊不直接對應。

OQL: Select子句: 1. 可以使用*,檢視結果物件的引用例項。 select * from java.util.Vector v 2. 可以指定物件的屬性進行輸出,使用"OBJECTS"關鍵字,可以將返回結果集中的項以物件的形式顯示。 select objects v.elementData from java.util.Vector v

From子句: 可以指定類名、正則表示式,可以查詢某個類的所有子類例項。 select * from java.lang.String s select * from ".*Stu.*" select * from instanceof java.util.AbstractCollection 可以查詢實本身的資訊 select * from objects java.lang.String

OQL中可以訪問堆內物件的屬性,也可以訪問堆內代理物件的屬性。 select toString(f.path.value) from java.io.File f select [email protected] from java.lang.String s

在Java虛擬機器的實現中每個物件都有一個物件頭,用於儲存物件的系統資訊。物件頭中有一個稱為Mark Word的部分,它是實現鎖的關鍵。在32位系統中,Mark Word為一個32位的資料,在64位系統中,它佔64位,它是一個多功能的資料區,可以存放物件的雜湊值、物件年齡、鎖的指標等資訊。

偏向鎖: 偏向鎖是JDK1.6提出的一種鎖優化方式。其核心思想是,如果程式沒有競爭,則取消之前已經取得鎖的執行緒同步操作。也就是說,若某一鎖被執行緒獲取後,便進入偏向模式,當執行緒再次請求這個鎖時,無需再進行相關的同步操作,從而節省了操作時間。如果在此之間有其他執行緒進行了鎖請求,則鎖退出偏向模式。在JVM中使用-XX:+UseBiasedLocking可以設定啟用偏向鎖。 當該執行緒再次嘗試獲得鎖時,通過Mark Word的執行緒資訊就可以判斷當前執行緒是否持有偏向鎖。偏向鎖在少競爭的情況下,對系統性能有一定幫助。

輕量級鎖: 使用輕量級鎖時,不需要申請互斥量,僅僅將Mark Word中的部分位元組CAS更新指向執行緒棧中的Lock Record,如果更新成功,則輕量級鎖獲取成功,記錄鎖狀態為輕量級鎖;否則,說明已經有執行緒獲得了輕量級鎖,目前發生了鎖競爭(不適合繼續使用輕量級鎖),接下來會發生鎖膨脹。

鎖膨脹後,進入ObjectMonitor的enter(),執行緒很可能會在作業系統層面被掛起,這樣執行緒上下文切換的效能損失就比較大。因此,在鎖膨脹之後,虛擬機器會做最後的爭取,希望執行緒可以儘快進入臨界區而避免被作業系統掛起,一種較為有效的手段就是使用自旋鎖。 自旋鎖可以使執行緒在沒有取得鎖時 ,不被掛起,而轉而去執行一個空迴圈(即所謂的自旋),在若干個空迴圈後,執行緒如果可以獲得鎖,則繼續執行。若執行緒依然不能獲得鎖,才會被掛起。 在JDK1.7中,自旋鎖的引數被取消,虛擬機器不再支援由使用者配置自旋鎖。自旋鎖總是會執行,自旋次數也由虛擬機器自行調整。

鎖消除是Java虛擬機器在JIT編譯時,通過對執行上下文的掃描,去除不可能存在共享資源競爭的鎖。通過鎖消除,可以節省毫無意義的請求鎖時間。

鎖在應用層的優化思路: 1. 減少鎖持有時間。如果執行緒持有鎖的時間很長,那麼相對的,鎖的競爭程度也就越激烈。因此,在程式開發過程中,應該儘可能地減少對某個鎖的佔有時間,以減少執行緒間互斥可能。 2. 減小鎖粒度,就是指縮小鎖定物件的範圍,從而減少鎖衝突的可能性。例如ConcurrentHashMap的put方法。ConcurrentHashMap將整個HashMap分成若干個段(Segment),每個段都是一個子HashMap. 3. 鎖分離,是減小鎖粒度的一個特例,它根據應用程式的功能特點,將一個獨佔鎖分成多個鎖,一個典型的案例就是java.util.concurrent.LinkedBlockingQueue的實現。 4. 鎖粗化,為了保證多執行緒間的有效併發,會要求每個執行緒持有鎖的時間儘量短,即在使用完公共資源後,應該立即釋放鎖。只有這樣,等待在這個鎖上的其他執行緒才能儘早地獲得資源執行任務。但是,凡事都有一個度,如果對同一個鎖不停地進行請求、同步和釋放,其本身也會消耗系統寶貴的資源,反而不利於效能的優化。

無鎖: CAS:CAS演算法(Compare And Swap)的過程是這樣:它包含3個引數(V,E,N)。V表示要更新的變數,E表示預期值,N表示新值。僅當V值等於E值時,才會將V的值設為N,如果V值和E值不同,則說明已經有其他執行緒做了更新,則當前執行緒什麼都不做。最後,CAS返回當前V的真實值。 原子操作:為了能讓CAS操作被Java應用程式充分使用,在JDK的java.util.concurrent.atomic包下,有一組使用無鎖演算法實現的原子操作類,主要有AtomicInteger, AtomicIntegerArray, AtomicLong, AtomicLongArray和AtomicReference等。

Java記憶體模型: 1. 原子性,在32位Java虛擬機器系統中,對於long和double的賦值讀取,由於long和double長度為64位,無法一次性操作,因此對於他們的操作都不是原子的,在併發環境下,可能會出現一些意想不到的錯誤。可以將他們定義為volatile解決。 2. 有序性,CPU可能會對目標指令進行重排。在一個執行緒中觀察另外一個執行緒的操作,我們會發現,被觀察執行緒的指令順序和預期情況不符。可以在方法上新增synchronized關鍵字解決。 3. 可見性,可見性是指當一個執行緒修改了一個變數的值,在另外一個執行緒中可以馬上得知這個修改。部分變數的值可能會放入到暫存器或高速緩衝(Cache)中,而每個CPU都擁有獨立的暫存器和Cache,從而導致其他執行緒無法立即發現這個修改。可以使用volatile關鍵字解決。