論文:Eliminating Synchronization-Related Atomic Operations with Biased Locking and Bulk Rebiasing
盡量直譯。
1. Background and Motivation
A class of optimizations which can be termed lightweight locking [1, 2, 5] are focused on avoiding as much as possible the use of “heavy-weight” operating system mutexes and condition variables to implement Java monitors. The assumption behind these techniques is that most lock acquisitions in real programs are uncontended. Lightweight locking techniques use atomic operations upon monitor entry, and sometimes upon exit, to ensure correct synchronization. These techniques fall back to using OS mutexes and condition variables when contention occurs.
一類稱作輕量級鎖定的優(yōu)化,關注的是,盡量避免使用“重量級”的操作系統(tǒng)互斥鎖和條件變量來實現(xiàn) Java 監(jiān)視器。這些技術背后的假設是,在實際程序中,大多數(shù)鎖的獲取是沒有爭用的。輕量級鎖定技術在進入監(jiān)視器時使用原子操作,有時退出監(jiān)視器也使用原子操作,以確保正確的同步。當發(fā)生爭用時,這些技術又退回使用 OS 的互斥鎖和條件變量。
A related class of optimizations which can be termed biased locking [3, 7, 9] rely on the further property that not only are most monitors uncontended, they are only entered and exited by one thread during the lifetime of the monitor. Such monitors may be profitably biased toward the owning thread, allowing that thread to enter and exit the monitor without using atomic operations. If another thread attempts to enter a biased monitor, even if no contention occurs, a relatively expensive bias revocation operation must be performed. The profitability of such an optimization relies on the benefit of the elimination of atomic operations being higher than the penalty of revocation.
一類相關的優(yōu)化稱為偏向鎖定,它依賴于進一步的屬性,即大多數(shù)監(jiān)視器不僅是沒有爭用的,而且在它們的生命周期中僅有一個線程進入和退出。這樣的監(jiān)視器可以偏向于擁有它的線程,允許該線程在不使用原子操作的情況下進入和退出這個監(jiān)視器。如果有另一個線程嘗試進入一個偏向監(jiān)視器,即使沒有發(fā)生爭用,也必須執(zhí)行代價相對昂貴的偏向撤銷操作。這種優(yōu)化的收益依賴于消除原子操作的好處高于撤銷的懲罰。
This paper presents a novel technique for eliminating atomic operations associated with the Java language’s synchronization primitives called store-free biased locking (SFBL). It is similar to, and is inspired by, the lock reservation technique [9] and its refinements [12, 10]. The specific contributions of our work are:
- We build upon invariants preserved by the Java HotSpot VM to eliminate repeated stores to the object header. Store elimination makes it easier to transfer bias ownership between threads.
- We introduce bulk rebiasing and revocation to amortize the cost of per-object bias revocation while retaining the benefits of the optimization.
- An epoch-based mechanism which invalidates previously held biases facilitates the bulk transfer of bias ownership from one thread to another.
本文提出了一種新的技術,用于消除與 Java 語言的同步原語相關的原子操作,這個技術稱為無存儲偏向鎖定(SFBL)。它類似于,也啟發(fā)于,鎖保留技術及其改進。我們工作的具體貢獻是: - 我們基于 Java HotSpot VM 保留的不變量,來消除對象頭的重復存儲。存儲消除使得它可以更容易地在線程之間轉(zhuǎn)移偏向所有權。
- 我們引入批量重偏向和撤銷,以攤分每個對象撤銷偏向的成本,同時保留優(yōu)化的收益。
- 一個基于 epoch 的機制可以使得之前持有的偏向失效,從而方便地實現(xiàn)偏向所有權從一個線程到另一個線程的批量轉(zhuǎn)移。

2. Overview of Lightweight Locking in the Java HotSpot VM
The Java HotSpot VM uses a two-word object header. The first word is called the mark word and contains synchronization, garbage collection and hash code information. The second word points to the class of the object. See figure 1 for an overview of the layout and possible states of the mark word.
Java HotSpot VM 使用一個雙字的對象頭。第一個字稱為 mark word,包含同步、垃圾收集和哈希碼信息。第二個字指向?qū)ο蟮念?。有關 mark word 的布局和可能的狀態(tài)的概述,請參見圖1。
Our biased locking technique relies on three invariants. First, the locking primitives in the language must be mostly block-structured. Second, optimized compiled code, if it is produced by the virtual machine, must only be generated for methods with block-structured locking. Third, interpreted execution must detect unstructured locking precisely. We now show how these invariants are maintained in our VM.
我們的偏向鎖定技術依賴于三個不變量。首先,語言中的鎖原語必須主要是塊結(jié)構(gòu)的。其次,優(yōu)化編譯的代碼(如果它是由虛擬機生成的)必須僅為具有塊結(jié)構(gòu)鎖的方法生成。第三,解釋執(zhí)行必須精確地檢測非結(jié)構(gòu)化鎖定?,F(xiàn)在我們展示如何在 VM 中維護這些不變量。(綜合論文的描述,我理解的這段的意思是,JVM 必須要保證加鎖和解鎖成對出現(xiàn)。)
Whenever an object is lightweight locked by a monitorenter bytecode, a lock record is either implicitly or explicitly allocated on the stack of the thread performing the lock acquisition operation. The lock record holds the original value of the object’s mark word and also contains metadata necessary to identify which object is locked. During lock acquisition, the mark word is copied into the lock record (such a copy is called a displaced mark word), and an atomic compare-and-swap (CAS) operation is performed to attempt to make the object’s mark word point to the lock record. If the CAS succeeds, the current thread owns the lock. If it fails, because some other thread acquired the lock, a slow path is taken in which the lock is inflated, during which operation an OS mutex and condition variable are associated with the object. During the inflation process, the object’s mark word is updated with a CAS to point to a data structure containing pointers to the mutex and condition variable.
當一個對象通過 monitorenter 字節(jié)碼被輕量鎖定時,鎖記錄就會隱式或顯式地在線程(正在執(zhí)行鎖獲取操作的線程)的棧上分配。鎖記錄保存了對象 mark word 的原始值,還包含了用于標識被鎖對象所需的元數(shù)據(jù)(元數(shù)據(jù)就是描述數(shù)據(jù)的數(shù)據(jù))。在獲取鎖的過程中,將 mark word 復制到鎖記錄中(這個 mark word 副本叫 displaced mark word),并執(zhí)行 CAS 操作嘗試使對象 mark word 指向鎖記錄。如果 CAS 成功了,當前線程就擁有了這個鎖。如果失敗了,因為其它線程獲得了鎖,則鎖膨脹(膨脹過程中,OS 互斥鎖和條件變量會與該對象關聯(lián))。在鎖膨脹的過程中,對象 mark word 用 CAS 更新,以指向包含互斥鎖和條件變量指針的數(shù)據(jù)結(jié)構(gòu)。(這段話介紹了輕量級鎖定的過程)
During an unlock operation, an attempt is made to CAS the mark word, which should still point to the lock record, with the displaced mark word stored in the lock record. If the CAS succeeds, there was no contention for the monitor and lightweight locking remains in effect. If it fails, the lock was contended while it was held and a slow path is taken to properly release the lock and notify other threads waiting to acquire the lock.
在解鎖操作期間,嘗試使用存儲在鎖記錄中的 displaced mark word 來對 mark word(它應該仍然指向鎖記錄)進行 CAS。如果 CAS 成功了,那就沒有發(fā)生過監(jiān)視器爭用,并且輕量級鎖定仍然有效。如果它失敗了,鎖在被持有期間被爭用了,那么將會采取一個緩慢的方式來正確地釋放鎖,并且通知正在等待獲取鎖的其他線程。(這段話介紹了輕量級解鎖的過程)
Recursive locking is handled in a straightforward fashion. If during lightweight lock acquisition it is determined that the current thread already owns the lock by virtue of the object’s mark word pointing into its stack, a zero is stored into the on-stack lock record rather than the current value of the object’s mark word. If zero is seen in a lock record during an unlock operation, the object is known to be recursively locked by the current thread and no update of the object’s mark word occurs. The number of such lock records implicitly records the monitor recursion count. This is a significant property to the best of our knowledge not attained by most other JVMs.
遞歸鎖定以一種簡單的方式處理。如果在輕量級鎖獲取過程中,發(fā)現(xiàn)對象的 mark word 指向的是當前線程的棧,那就確定了當前線程擁有該鎖,則把 0 存入棧上的鎖記錄,而不是對象 mark word 的當前值中。如果在解鎖操作期間,在鎖記錄中看到 0,則知道該對象被當前線程遞歸鎖定,而且該對象的 mark word 沒有發(fā)生更新。這樣的鎖記錄的數(shù)量相當于隱式地記錄了該監(jiān)視器遞歸計數(shù)。據(jù)我們所知,這是一個大多數(shù)其它 JVM 沒有做到的重要的屬性。

3. Store-Free Biased Locking
Assuming the invariants in Section 2, the SFBL algorithm is simple to describe. When an object is allocated and biasing is enabled for its data type (discussed further in Section 4), a bias pattern is placed in the mark word indicating that the object is biasable (figure 1). The Java HotSpot VM uses the value 0x5 in the low three bits of the mark word as the bias pattern.
在第 2 節(jié)中不變量的假設下,SFBL 算法描述起來很簡單。當一個對象被分配,并且在它的數(shù)據(jù)類型中啟用了偏向(在第 4 節(jié)進一步討論)。放在 mark word 中的偏向模式表明該對象是可偏向的(圖一)。Java HotSpot VM 令 mark word 低三位值為 0x5 時,為偏向模式。
The thread ID may be a direct pointer to the JVM’s internal representation of the current thread, suitably aligned so that the low bits are zero. Alternatively, a dense numbering scheme may be used to allow better packing of thread IDs and potentially more fields in the biasable object mark word.
線程 ID 可以是一個直接指針,指向當前線程(適當對齊,使得指針低位為 0)在 JVM 內(nèi)部的表示?;蛘撸梢允褂妹芗幪柗桨竵砀玫卮虬€程 ID,并可能在可偏向?qū)ο?mark word 中包含更多字段。
During lock acquisition of a biasable but unbiased object, an attempt is made to CAS the current thread ID into the mark word’s thread ID field. If this CAS succeeds, the object is now biased toward the current thread, as in figure 2. The current thread becomes the bias owner. The bias pattern remains in the mark word alongside the thread ID.
在鎖定獲取一個可偏向但無偏向的對象時,使用 CAS 嘗試將當前線程 ID 放入 mark word 的 thread ID 字段。如果這個 CAS 操作成功了,該對象現(xiàn)在就偏向于當前線程,如圖 2 所示。當前線程成為這個偏向的所有者。偏向模式保留在 mark word 中 thread ID 的旁邊。
If the CAS fails, another thread is the bias owner, so that thread’s bias must be revoked. The state of the object will be made to appear as if it had been locked by the bias owner using the JVM’s underlying lightweight locking scheme. To do this, the thread attempting to bias the object toward itself must manipulate the stack of the bias owner. To enable this a global safepoint is reached, at which point no thread is executing bytecodes. The bias owner’s stack is walked and the lock records associated with the object are filled in with the values that would have been produced had lightweight locking been used to lock the object. Next, the object’s mark word is updated to point to the oldest associated lock record on the stack. Finally, the threads blocked on the safepoint are released. Note that if the lock were not actually held at the present moment in time by the bias owner, it would be correct to revert the object back to the “biasable but unbiased” state and re-attempt the CAS to acquire the bias. This possibility is discussed further in section 4.
如果 CAS 失敗了,另一個線程是偏向的所有者,因此必須撤銷該線程的偏向。對象的狀態(tài)將顯示為它被偏向所有者使用 JVM 的底層輕量級鎖定方案鎖定。為此,試圖使對象偏向自身的線程必須操作偏向所有者的棧。要實現(xiàn)這一點,需要達到一個全局安全點,此時沒有線程執(zhí)行字節(jié)碼。偏向所有者的棧被遍歷,與對象關聯(lián)的鎖記錄被填充進(如果使用輕量級鎖定來鎖對象,將會產(chǎn)生的)值。接下來,更新對象的 mark word,以指向棧上最古老的關聯(lián)鎖記錄。最后,在安全點上阻塞的線程被釋放。請注意,如果當前偏向所有者沒有實際持有鎖,那么將對象恢復到“可偏向但無偏向”的狀態(tài)并重新嘗試CAS以獲得偏向是正確的。第4節(jié)將進一步討論這種可能性。(這段描述了偏向失敗后的做法。)
If the CAS succeeded, subsequent lock acquisitions examine the object’s mark word. If the object is biasable and the bias owner is the current thread, the lock is acquired with no further work and no updates to the object header; the displaced mark word in the lock record on the stack is left uninitialized, since it will never be examined while the object is biasable. If the object is not biasable, lightweight locking and its fallback paths are used to acquire the lock. If the object is biasable but biased toward another thread, the CAS failure path described in the previous paragraph will be taken, including the associated bias revocation.
如果 CAS 成功了,后面的鎖獲取將檢查對象的 mark word。如果對象是可偏向的,并且偏向的所有者是當前線程,那么鎖將被獲取,而不需要進一步的工作,也不需要更新對象頭;棧上的鎖記錄中 displaced mark word 未初始化,因為當對象是可偏向的時,它將永遠不會被檢查。如果對象是不可偏向的,則使用輕量級鎖及其回退方式來獲取鎖。如果對象是可偏向的,但偏向于另一個線程,則將采用上一段描述的 CAS 失敗處理方式,包括相關的偏向撤銷。(這段描述了偏向成功后,后面鎖獲取時的做法。最后一句例外,最后一句針對的是一個新的想獲取偏向鎖的線程。)
When an object is unlocked, the state of its mark word is tested to see if the bias pattern is still present. If it is, the unlock operation succeeds with no other tests. It is not even necessary to test whether the thread ID is equal to the current thread’s ID. If another thread had attempted to acquire the lock while the current thread was actually holding the lock and not just the bias, the bias revocation process would have ensured that the object’s mark word was reverted to the unbiasable state.
當一個對象被解鎖時,將測試其 mark word 的狀態(tài),以確定是否仍然處在偏向模式。如果是,解鎖操作成功,沒有其他測試。不需要測試是否 thread ID 等于當前線程的 ID。如果另一個線程嘗試過獲取鎖,而且這個鎖是當前線程實際正在持有的而不是僅僅是偏向的。另一個線程撤銷偏向的過程確保對象的 mark word 恢復到不可偏向狀態(tài)。
Since the SFBL unlock path does no error checking, the correctness of the unlock path hinges on the interpreter’s detection of unstructured locking. The lock records in interpreter activations ensure that the body of the monitorexit operation will not be executed if the object was not locked in the current activation. The guarantee of matched monitors in compiled code implies that no error checking is required in the SFBL unlock path in compiled code.
由于 SFBL 解鎖方式不進行錯誤檢查,所以解鎖方式的正確性取決于解釋器對非結(jié)構(gòu)化鎖的檢測。解釋器活動中的鎖記錄,確保當前活動中的對象沒有被鎖定,將不會執(zhí)行 monitorexit 操作的主體。已編譯代碼中對監(jiān)視器匹配的擔保,意味著在已編譯代碼中的 SFBL 解鎖方式不需要錯誤檢查。
Figure 2 shows the state transitions of the mark word of an object under the biased locking algorithm. The bulk rebiasing edge, which is described further in sections 4 and 5, is only an effective, not an actual, transition and does not necessarily involve an update to the object’s mark word. Recursive locking edges, which update the on-stack lock records but not the mark word, and the heavyweight locking state, which involves contention with one or more other threads, are omitted for clarity.
圖 2 顯示了在偏向鎖定算法下對象的 mark word 的狀態(tài)轉(zhuǎn)換。在第 4 節(jié)和第 5 節(jié)中進一步描述的批量重偏向僅僅是一種有效的轉(zhuǎn)換,而不是實際的轉(zhuǎn)換,并且不需要更新對象的 mark word。為了清晰起見,這里省略了遞歸鎖定(更新棧上的鎖記錄,但不更新 mark word)和重量級鎖定狀態(tài)(涉及與一個或多個其他線程的爭用)。
4. Bulk Rebiasing and Revocation
Analysis of execution logs of SFBL for the SPECjvm98, SPECjbb2000, SPECjbb2005 and SciMark benchmark suites yields two insights. First, there are certain objects for which biased locking is obviously unpro?table, such as producer-consumer queues where two or more threads are involved. Such objects necessarily have lock contention, and many such objects may be allocated during a program’s execution. It would be ideal to be able to identify such objects and disable biased locking only for them. Second, there are situations in which the ability to rebias a set of objects to another thread is pro?table, in particular when one thread allocates many objects and performs an initial synchronization operation on each, but another thread performs subsequent work on them.
對 SPECjvm98、SPECjbb2000、SPECjbb2005 和 SciMark 基準測試套件的 SFBL 執(zhí)行日志的分析得出了兩個結(jié)論。首先,對于某些對象,偏向鎖定顯然是無利可圖的,例如涉及兩個或多個線程的生產(chǎn)者-消費者隊列。這樣的對象必然存在鎖爭用,并且在程序執(zhí)行過程中可能會分配許多這樣的對象。能夠識別這樣的對象并僅為它們禁用偏向鎖定是理想的。其次,在某些情況下,將一組對象重偏向到另一個線程是有好處的,特別是當一個線程分配許多對象并對每個對象執(zhí)行一個初始同步操作,而另一個線程對這些對象執(zhí)行后續(xù)操作時。(這段描述了,批量撤銷和批量重偏向針對的場景。注意:HotSpot VM 的批量撤銷和批量重偏向針對的都是同類對象的操作。)
When attempting to selectively disable biased locking, we must be able to identify objects for which it is unpro?table. If one were able to associate an object with its allocation site, one might ?nd patterns of shared objects; for example, all objects allocated at a particular site might seem to be shared between multiple threads. Experiments indicate this correlation is present in many programs[6]. Being able to selectively disable the insertion of the biasable mark word at that site would be ideal. However, due to its overhead, allocation site tracking is to the best of our knowledge not currently exploited in production JVMs.
當嘗試有選擇地禁用偏向鎖定時,我們必須能夠識別出不適合偏向的對象。如果能夠?qū)ο笈c其分配位置關聯(lián)起來,就可能發(fā)現(xiàn)共享對象的模式;例如,在特定位置上分配的所有對象似乎都在多個線程之間共享。實驗表明,這種相關性存在于許多程序中。能夠有選擇地在該位置上禁用可偏向 mark word 的插入將是理想的。但是,由于它的開銷,就我們所知,還沒有 JVM 產(chǎn)品使用分配位置追蹤。
We have found empirically that selectively disabling SFBL for a particular data type is a reasonable way to avoid unpro?table situations. We therefore amortize the cost of rebiasing and individual object bias revocation by performing such rebiasing and revoking in bulk on a per-data-type basis.
我們已經(jīng)從經(jīng)驗中發(fā)現(xiàn),對特定數(shù)據(jù)類型選擇性地禁用 SFBL 是避免無利可圖情況的合理方法。因此,我們通過在每個數(shù)據(jù)類型的基礎上執(zhí)行這種批量重偏向和撤銷,來平攤重偏向和單個對象偏向撤銷的成本。
Heuristics are added to the basic SFBL algorithm to estimate the cost of individual bias revocations on a per-data-type basis. When the cost exceeds a certain threshold, a bulk rebias operation is attempted. All biasable instances of the data type have their bias owner reset, so that the next thread to lock the object will reacquire the bias. Any biasable instance currently locked by a thread may optionally have its bias revoked or left alone.
在基本的 SFBL 算法中添加了啟發(fā)式算法,以估計基于每個數(shù)據(jù)類型的單個偏向撤銷成本。當成本超過某個閾值時,將嘗試進行批量重偏向操作。數(shù)據(jù)類型的所有可偏向?qū)嵗钠蛩姓叨紝⒅刂茫员阆乱粋€鎖定對象的線程將重新獲得偏向。任何當前被線程鎖定的可偏向?qū)嵗伎梢杂羞x擇地撤銷或保持其偏向。(在 JDK8 HotSpot VM 中,一個類型的單個偏向撤銷次數(shù)達到一定的閾值(默認 20 次),就可以對該類型對象進行批量重偏向。如果該類型距上次批量重偏向的時間超過設定閾值(默認 25000 毫秒,即 25 秒),則會重置單個偏向撤銷的計數(shù),后面計數(shù)重新達到 20 后能夠再次進行批量重偏向。)
If bias revocations for individual instances of a given data type persist after one or more bulk rebias operations, a bulk revocation is performed. The mark words of all biasable instances of the data type are reset to the lightweight locking algorithm’s initial value. For currently-locked and biasable instances, the appropriate lock records are written to the stack, and their mark words are adjusted to point to the oldest lock record. Further, SFBL is disabled for any newly allocated instances of the data type.
如果給定數(shù)據(jù)類型的單個實例的偏向撤銷在一次或多次批量重偏向操作之后仍然存在,則執(zhí)行批量撤銷。該數(shù)據(jù)類型的所有可偏向?qū)嵗?mark word 被重置為輕量級鎖定算法的初始值。對于當前已鎖定并且是可偏向的實例,將適當?shù)逆i記錄寫入棧,并調(diào)整它們的 mark word 以指向最舊的鎖記錄。此外,SFBL 將禁用該數(shù)據(jù)類型任何新分配實例的可偏向。(在 JDK8 HotSpot VM 中,簡單來說,一個類型的對象在短時間(默認 25 秒)內(nèi)頻繁發(fā)生偏向撤銷(默認 40 次),就會觸發(fā)該類型的批量撤銷。)
The most obvious way of ?nding all instances of a certain data type is to walk through the object heap, which is how these techniques were initially implemented (Section 5 describes the current implementation). Despite the computational expense involved, bulk rebiasing and revocation are surprisingly effective.
查找某個數(shù)據(jù)類型的所有實例的最明顯的方法是遍歷對象堆,這就是這些技術最初是如何實現(xiàn)的(第5部分描述了當前的實現(xiàn))。盡管涉及到計算開銷,但批量重偏向和撤銷的效率驚人。
Figure 3 illustrates the bene?ts of the bulk revocation and rebiasing heuristics compared to the basic biased locking algorithm. The javac sub-benchmark from SPECjvm98 computes many identity hash codes, forcing bias revocation of the affected objects since there are no bits available to store the hash code in the biasable state (see ?gure 1). Bulk revocation bene?ts this and similar situations, here in particular because our early implementations performed relatively inef?cient bias revocation in this case. SPECjbb2000 and SPECjbb2005 transfer a certain number of objects between threads as each warehouse is added to the benchmark, not enough to impact scores greatly but enough to trigger the bulk revocation heuristic. The addition of bulk rebiasing, which is then triggered at the time of addition of each warehouse, reclaims the gains to be had. Note that the addition of both bulk revocation and rebiasing does not reduce the peak performance of biased locking compared to the basic algorithm without these operations. This is discussed further in Section 7.
圖3說明了與基本的偏向鎖定算法相比,批量撤銷和再偏向啟發(fā)式算法的優(yōu)點。SPECjvm98 的 javac 子基準計算了許多對象哈希碼,強制取消受影響對象的偏向,因為在可偏向狀態(tài)下沒有可用的位來存儲哈希碼(見圖 1)。批量撤銷有利于這種情況和類似的情況,尤其是在這里,因為我們的早期實現(xiàn)在這種情況下執(zhí)行的偏向撤銷效率相對較低。SPECjbb2000 和 SPECjbb2005 在線程之間傳輸一定數(shù)量的對象,因為每個數(shù)據(jù)倉庫都被添加到基準中,這并不足以影響分數(shù),但足以觸發(fā)批量撤銷。添加批量重偏向,然后在每個倉庫添加時觸發(fā),收回要獲得的收益。請注意,與不進行這些操作的基本算法相比,添加批量撤銷和重偏向并沒有降低偏向鎖定的峰值性能。這將在第7節(jié)進一步討論。

5. Epoch-Based Bulk Rebiasing and Revocation
Though walking the object heap to implement bulk rebias and revocation algorithms is workable for relatively small heaps, it does not scale well as the heap grows. To address this problem, we introduce the concept of an epoch, a timestamp indicating the validity of the bias. As shown in ?gure 1, the epoch is a bit?eld in the mark word of biasable instances. Each data type has a corresponding epoch as long as the data type is biasable. An object is now considered biased toward a thread T if both the bias owner in the mark word is T, and the epoch of the instance is equal to the epoch of the data type.
雖然遍歷對象堆來實現(xiàn)批量重偏向和撤銷算法對于相對較小的堆是可行的,但是它不能隨著堆的增長而很好地擴展。為了解決這個問題,我們引入了 epoch 的概念,一個表示偏向有效性的時間戳。如圖 1 所示,epoch 是可偏向?qū)嵗?mark word 中的一個位字段。每個可偏向的數(shù)據(jù)類型都有相應的 epoch。如果一個對象的 mark word 中的偏向所有者是 T,而且對象的 epoch 等于其數(shù)據(jù)類型的 epoch,則認為這個對象現(xiàn)在是偏向于線程 T 的。
With this scheme, bulk rebiasing of objects of class C becomes much less costly. We still stop all mutator threads at a safepoint; without stopping the mutator threads we cannot reliably tell whether or not a biased object is currently locked. The thread performing the rebiasing:
- Increments the epoch number of class C. This is a ?xed-width integer, with the same bit-width in the class as in the object headers. Thus, the increment operation may cause wrapping, but as we will argue below, this does not compromise correctness.
- Scans all thread stacks to locate objects of class C that are currently locked, updating their bias epochs to the new current bias epoch for class C. Alternatively, based on heuristic consideration, these objects’ biases could be revoked.
使用這種方案,類 C 的對象的批量重偏向的成本將大大降低。我們?nèi)匀粫谝粋€安全點停止所有的 mutator 線程;如果不停止 mutator 線程,我們就無法可靠地判斷當前是否鎖定了一個偏向?qū)ο蟆>€程執(zhí)行重偏向:
- 增加類 C 的 epoch。這是一個固定寬度的整數(shù),類中的位寬與對象頭中的位寬相同。因此,增加操作可能會導致溢出,但正如我們將在下面討論的,這并不會影響正確性。
- 掃描所有線程的棧,以定位類 C 中當前被鎖定的對象,將它們的 epoch 更新為類 C 當前的 epoch?;蛘撸趩l(fā)式的考慮,可以撤銷這些對象的偏向。
No heap scan is necessary; objects whose epoch numbers were not changed will, for the most part, now have a different epoch number than their class, and will be considered to be in the biasable but unbiased state.
不需要堆掃描那些 epoch 沒有改變的對象,在大多數(shù)情況下,如果對象的 epoch 值和它的類的 epoch 值不同,則可以認為這個對象處于可偏向但未偏向的狀態(tài)。(如果對象的 epoch 和 其類型的 epoch 不同,則可以直接對該對象進行重偏向。)
The pseudocode for the lock-acquisition operation then looks much like:

Above we made the quali?cation that incrementing a class’s bias epoch will “for the most part” rebias all objects of the given class. This quali?cation is necessary because of the ?nite width of the epoch ?eld, which allows integer wrapping. If the epoch ?eld is N bits wide, and X is an object of type T, then if 2?N bulk rebiasing operations for class T occur without any lock operation updating the bias epoch of X to the current epoch, then it will appear that X is again biased in the current epoch, that is, that its bias is valid. Note that this is purely a performance concern – it is perfectly permissible, from a correctness viewpoint, to consider X biased. It may mean that if a thread other than the bias holder attempts to lock X, an individual bias revocation operation may be required. But a suf?ciently large value of N can decrease the frequency of this situation signi?cantly: objects that are actually locked between one epoch and the next have their epoch updated to the current epoch, so this situation only occurs with infrequently-locked objects. Further, we could arrange for operations that naturally visit all live objects, namely garbage collection, to normalize lock states, converting biased objects with invalid epochs into biasable-but-unbiased objects. (If done in a stop-world collection this can be done with non-atomic stores; in a concurrent marker, however, the lock word would have to be updated with an atomic operation, since the marking thread would potentially compete with mutator threads to modify the lock word.) Therefore, wrapping issues could also be prevented by choosing N large enough to make it highly likely that a full-heap garbage-collection would occur before 2?N bulk rebias operations for a given type can occur.
上面我們做了這樣的限定:增加一個類的 epoch 將“在大部分情況下”重偏向該類的所有對象。這個限定是必要的,因為 epoch 字段的寬度是有限的,它允許整數(shù)溢出。如果 epoch 字段是 N 位寬,X 是一個類型 T 的對象,然后如果對類 T 有 2^N 次批量重偏向操作,而且沒有任何鎖定操作更新 X 的 epoch 到當前類的 epoch,那么就會出現(xiàn) X 在當前 epoch 再次是已偏向的,也就是說它的偏向是有效的。請注意,這純粹是一個性能問題—從正確性的角度來看,完全允許考慮 X 已偏向。這可能意味著,如果一個非偏向持有者的線程試圖鎖定 X,可能需要一個單獨的偏向撤銷操作。但是足夠大的 N 值會顯著降低這種情況的發(fā)生頻率:實際上在一個 epoch 和下一個 epoch 之間被鎖定的對象會將它們的 epoch 更新到當前的 epoch,所以這種情況只會發(fā)生在不經(jīng)常被鎖定的對象上。此外,我們可以安排自然訪問所有活動對象的操作,即垃圾收集,以規(guī)范化鎖狀態(tài),將帶有無效 epoch 的有偏向?qū)ο筠D(zhuǎn)換為可偏向但未偏向的對象。(如果是在一個 stop-world 收集中完成的,那么可以使用非原子操作更新;然而,在并發(fā)標記中,lock word 必須用原子操作更新,因為標記線程可能會與 mutator 線程競爭來修改 lock word。因此,溢出問題可以通過選擇足夠大的 N 來阻止,使得很有可能一個全堆垃圾收集發(fā)生在一個給定類型發(fā)生 2^N 次批量重偏向之前。
In practice, wrapping of the epoch ?eld can be ignored. Benchmarking has not uncovered any situations where individual bias revocations are provoked due to epoch over?ow. The current implementation of biased locking in the Java HotSpot VM normalizes object headers during GC, so the mark words of biasable objects with invalid epochs are reverted to the unbiased state. This is done purely to reduce the number of mark words preserved during GC, not to counteract epoch over?ow.
在實踐中,可以忽略 epoch 字段的溢出?;鶞蕼y試還沒有發(fā)現(xiàn)任何情況下,單獨偏向撤銷是由 epoch 溢出引起的。Java HotSpot VM 中偏向鎖定的當前實現(xiàn)在 GC 期間將對象頭進行規(guī)范化,因此帶有無效 epoch 的可偏向?qū)ο蟮?mark word 將恢復到無偏向狀態(tài)。這樣做純粹是為了減少在 GC 期間已偏向 mark word 的數(shù)量,而不是為了抵消 epoch 溢出。
It is a straightforward extension to support bulk revocation of biases of a given data type. Recall that in bulk revocation, unlike bulk rebiasing, it is desired to completely disable the biased locking optimization for the data type, instead of allowing the object to be potentially rebiased to a new thread. Rather than incrementing the epoch in the data type, the “biasable” property for that data type may be disabled, and a dynamic test of this property added to the lock sequence:

它是一個支持批量撤銷給定數(shù)據(jù)類型的偏向的簡單擴展?;叵胍幌?,在批量撤銷中,與批量重偏向不同,我們希望完全禁用數(shù)據(jù)類型的偏向鎖定優(yōu)化,而不是允許對象可能重偏向到一個新線程。與在數(shù)據(jù)類型中遞增 epoch 不同,該數(shù)據(jù)類型的 “biasable” 屬性可能會被禁用,并且該屬性的一個動態(tài)測試將被添加到鎖定代碼中:(通過在鎖定時判斷類的可偏向?qū)傩詠砼袛鄬ο笃虻挠行浴?/strong>)
This variant of the lock sequence is the one currently implemented in the Java HotSpot VM.
鎖代碼的這種變體是目前在 Java HotSpot VM 中實現(xiàn)的。
Epoch-based rebiasing and revocation may also be extended to rebias objects at a granularity between the instance and class level. For example, we might distinguish between objects of a given class based on their allocation site; JIT-generated allocation code could be modi?ed to insert an allocation site identi?er in the object header. Each allocation site could have its own epoch, and the locking sequence could check the appropriate epoch for the object:

基于 epoch 的重偏向和撤銷也可以擴展到實例級和類級之間的粒度上的可偏向?qū)ο?。例如,我們可以根?jù)分配位置來區(qū)分給定類的對象;可以修改 jit 生成的分配代碼,以便在對象頭中插入分配位置標識符。每個分配位置都可以有自己的 epoch,鎖序列可以檢查對象對應的 epoch:
To simplify the allocation path for new instances as well as storage of the per-data-type epochs, a prototype mark word is kept in each data type. This is the value to which the mark word of new instances will be set. The epoch is stored in the prototype mark word as long as the prototype is biasable.
為了簡化新實例的分配方法和每個數(shù)據(jù)類型 epoch 的存儲,在每個數(shù)據(jù)類型中都保留一個原型標記字。這是新實例的標記字將被設置的值。只要原型是可偏向的,epoch 就存儲在原型標記字中。
In practice, a single logical XOR operation in assembly code computes the bitwise difference between the instance’s mark word and the prototype mark word of the data type. A sequence of tests are performed on the result of the XOR to determine whether the bias is held by the current thread and currently valid, whether the epoch has expired, whether the data type is no longer biasable, or whether the bias is assumed not held, and the system reacts appropriately. Listing 4 shows the complete SPARC assembly code for the lock acquisition path of SFBL with epochs.
在實踐中,匯編代碼中的一個邏輯異或操作按位計算實例的標記字和數(shù)據(jù)類型的原型標記字之間的差異。測試序列進行異或的結(jié)果,確定偏向被當前線程持有并且是有效的,epoch 是否已經(jīng)過期,是否數(shù)據(jù)類型不再允許偏向,或偏向是否認為被持有,并且系統(tǒng)會做出適當?shù)姆磻?。清?4 顯示了帶有 epoch 的 SFBL 鎖獲取方法的完整SPARC 匯編代碼。