精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

詳細介紹synchronized和Object的關鍵方法和虛擬機實現原理

Linux閱碼場 ? 來源:內核工匠 ? 2023-03-13 10:06 ? 次閱讀

一、前言

編程過程中經常會遇到線程的同步問題,Java 中對同步問題的解決方案比較多(synchronized、JUC、原子操作、volatile、條件變量等),其中synchronized 最方便、簡單易用,也是java 編程中使用最多的臨界區保護方案。本文主要講述對象鎖的相關知識,詳細介紹synchronized 和Object 的關鍵方法的虛擬機實現原理。

二、Java 對象鎖的使用方式

2.1 實例方法的同步

0125b334-bfd4-11ed-bfe3-dac502259ad0.png

synchronized 修飾實例方法,該同步僅對當前對象的該方法起作用,同一時間只能有一個線程可以進入該對象的此方法。對于不同對象的此函數,無法做到互斥保護。

2.2 靜態方法的同步

012e647a-bfd4-11ed-bfe3-dac502259ad0.png

synchronized 修飾靜態方法,該同步對當前類對象的該方法起作用,同一時間只能有一個線程可以進入該方法。

2.3 代碼塊的同步

01357d14-bfd4-11ed-bfe3-dac502259ad0.png

在大多少情況下,并不需要對整個方法進行保護,當synchronized 修飾代碼塊時,該代碼塊的訪問依賴于object 對象鎖的互斥訪問,同一時間只能有一個線程持有object 對象鎖。

更準確的來講,synchronized 關鍵字是依賴于對象鎖而生效的,每個synchronized 同步塊開始的地方都會生成monitor-enter obj指令,同步塊結束的地方生成monitor-exit obj 的指令,其中obj 為用于控制互斥訪問的對象。同一時間只能有一個線程持有obj 的對象鎖。在2.1 中synchronized 依賴的是實列對象,2.2 中synchronized 依賴的是類對象,2.3 中synchronized 依賴的是object 對象。

當一個對象控制多個代碼塊時,多個代碼塊也是互斥訪問,如下面代碼:

013f31a6-bfd4-11ed-bfe3-dac502259ad0.png

代碼塊①和代碼塊② 雖然在兩個函數中,但是synchronized 依賴的對象都為object,這兩個代碼塊也是互斥訪問。

2.4 Object wait() 和notify() 使用方法

Object 作為所有類的基類,都實現了object方法。典型的用法如下:

014808ee-bfd4-11ed-bfe3-dac502259ad0.png

thread 1持有object 對象鎖,并調用object.wait() 方法后,則該線程進入WAITING狀態,并釋放object 對象鎖,等待其它線程來喚醒它。

當thread 2 持有object 對象鎖,并調用object.notify()方法后,喚醒thread 1,thread 1

重新獲得object 對象鎖繼續執行。Object類方法說明:

0153dcbe-bfd4-11ed-bfe3-dac502259ad0.png

三、Android對象內存結構

3.1 對象內存結構

015b436e-bfd4-11ed-bfe3-dac502259ad0.png

一個類的實例對象內存主要由3部分組成:

1). 對象頭:對象頭包括kclass_和monitor_兩個字段,其中kclass_ 存放指向類對象的指針,通過該指針可以找到該對象對應的類,monitor_ 用于存放對象運行時的標識數據,例如: GC 標志位、哈希碼、鎖狀態等信息,后面詳細分析。

2). 實例數據,該部分存放實例變量值,父類實例變量值在前,子類在后,且實例變量值按照如下順序進行排序:

01641c32-bfd4-11ed-bfe3-dac502259ad0.png

3).對齊填充,對象在內存中是按照8byte 對齊的,如果實例數據部分沒有按照8byte對齊,則填充為8byte 對齊。

3.2 monitor_ 字段分析

monitor_ 字段定義在art/runtime/mirror/object.h,類型為uint32_t,主要有下面3個操作函數。

01709f48-bfd4-11ed-bfe3-dac502259ad0.png

操作函數中SetLockWord和CasLockWord函數的入參或GetLockWord函數的返回值都包含LockWord 變量,對monitor_ 字段的操作是通過LockWord 的值進行的。

下面再來看LockWord 定義:

LockWord 類的定義在art/runtime/lock_word.h 文件中,從注釋中可以看到LockWord的使用主要有4種狀態,如下:

017afa24-bfd4-11ed-bfe3-dac502259ad0.png

LockWord 的設計非常精妙,一個32 位數據的每一位都充分利用,而且很好的區分了不同狀態。下面對各狀態進行詳細說明:

unlocked/thin 狀態下31-30 bit 為00,默認狀態下為unlocked 狀態,當對象進行線程同步時變成thin lock 狀態,27-16bit 記錄了thin lock重入的次數,15-0 bit 記錄了持有該thin lock的線程ID。

fat lock狀態下31-30 bit 為01,當對象鎖在thin lock狀態,且有新的(非owner)線程與其競爭,經過適當的等待期(sched_yield調用、循環獲取thin lock 狀態)后依然無法拿到鎖,則轉換為fat lock 狀態,并為該對象分配一個Monitor 資源。

hash state狀態下31-30 bit 為10,在27-0 bit 存儲對象的hash code,當在其它模式下,hash code 會存儲在該對象關聯的Monitor 對象中。

forwarding address state 狀態下31-30 bit 為11,在concurrent copying GC 的copy 階段,當一個對象被拷貝后,指向拷貝后的對象地址,當線程訪問到該對象后,通過該轉發地址,訪問新的對象。

第29 位為mark bit,通過該bit位可以快速判斷是否標記過,避免重復標記。

第28 位為read barrier bit,如果對象LockWorkd的該bit 被設置,則在訪問該對象的成員時會進入慢速路徑,判斷對象是不是需要更新,如果需要更新,則返回拷貝后的對象地址。

四、對象鎖代碼分析

4.1 首先我們看一段代碼

0183f016-bfd4-11ed-bfe3-dac502259ad0.png

這段代碼比較簡單,主要有下面兩個核心點:

1). 在主線程執行的過程中,用obj 對象進行線程同步,并調用obj.wait()函數,使線程阻塞在了obj 對象鎖上等待喚醒。

2). main函數中創建匿名線程,該線程首先sleep 2000ms,然后喚醒阻塞在obj 對象鎖上線程。

4.2編譯TestDemo.java,命令如下:

01934f52-bfd4-11ed-bfe3-dac502259ad0.png

1).Javac 將TestDemo.java 文件編譯生成TestDemo*.class文件,java 編譯過程中每個類會生成一個class 文件。

2).d8 命令將TestDemo*.class 文件通過編譯、重構、重排、壓縮、混淆后生成對應的dex (Dalvik Executable file)格式文件。

3).dexdump.exe命令可以查看dex 文件格式的詳細信息,如校驗信息、dex 頭信息、生成dex 的CFG 信息、dex 的反匯編信息等,詳細使用方法可以通過dexdump.exe –help 命令查看

通過dexdump.exe –d classes.dex 查看反匯編

其中run 方法指令信息如下:

019aa806-bfd4-11ed-bfe3-dac502259ad0.png

main 函數的指令信息如下:

01a6f476-bfd4-11ed-bfe3-dac502259ad0.png

對部分指令解析如下:

01b3ab30-bfd4-11ed-bfe3-dac502259ad0.png

本文重點分析monitor-enter、monitor-exit、Object.wait()、Object.notify()在虛擬機中的詳細實現。

4.3.Object.wait() 流程分析

Object.wait() 的調用關系如下:

01b9e626-bfd4-11ed-bfe3-dac502259ad0.png

Object 類是所有類的父類,任何類中都可以調用public 的wait() 方法,最終調用到虛擬機的monitor.cc 文件的wait 靜態方法,

01c49472-bfd4-11ed-bfe3-dac502259ad0.png

首先構造了一個操作obj 的Handle對象h_obj,通過ObjectWaitStart 函數通知jvmti 調試系統發生了JVMTI_EVENT_MONITOR_WAIT 事件。

JVMTI(JVM Tool Interface)是 Java 虛擬機所提供的 native 編程接口,可以用來開發并監控虛擬機,可以查看JVM內部的狀態,并控制JVM應用程序的執行。可實現的功能包括但不限于:調試、監控、線程分析、覆蓋率分析工具等。

01cf4ca0-bfd4-11ed-bfe3-dac502259ad0.png

首先獲得h_obj 對象的LockWord 字段,lock_word.GetState()函數獲得當前的鎖狀態,主要有下面幾種情況:

1).hash 或unlocked 狀態:

因為調用wait()方法必須持有對象鎖,所以不會出現這兩種狀態,如果出現則拋出IllegalMonitorStateException 異常。

2).thin lock 狀態:

當持有該對象鎖的線程不是要wait 的線程,也拋出IllegalMonitorStateException 異常,當持有鎖的線程與要wait 的線程一致,這時需要將thin lock inflate 為fat lock,inflate 的過程在monitor-enter 指令分析中分析。

當對象鎖inflate 為fat lock 狀態后,調用Monitor 對象的實例方法Wait讓線程進入sleep 狀態等待。

4.4 Object.notify() 流程分析

01d96622-bfd4-11ed-bfe3-dac502259ad0.png

這里我們直接分析DoNotify 函數:

01e4f622-bfd4-11ed-bfe3-dac502259ad0.png

通過lock_word.GetState() 獲得當前obj 對象的鎖狀態,主要有下面情況:

1) hash 或unlocked 狀態 :

拋出IllegalMonitorStateException 異常。

2).thin lock 狀態:

當持有該對象鎖的線程不是要notify 的線程,也拋出IllegalMonitorStateException 異常,當持有鎖的線程與要notify 的線程一致,這時說明沒有需要通知喚醒的線程,直接返回。

3).fat lock 狀態:

在Object.notify() 流程中參數notify_all 為false,則直接調用mon->Notify(self);通知喚醒等待線程。

4.5monitor-enter 流程分析

對于解釋執行和機器碼執行模式,最終都會調用到art/runtime/mirror/object-inl.h 文件Object 對象的MonitorEnter 函數。

01ef32cc-bfd4-11ed-bfe3-dac502259ad0.png

下面來分析Monitor類的靜態方法MonitorEnter 函數。

01f5be08-bfd4-11ed-bfe3-dac502259ad0.png

FakeLock 主要用于線程安全性檢查,主要在編譯期檢測

kExtraSpinIters 定義了當對象鎖被其它線程持有且為thin lock 時,競爭線程循環獲取鎖的次數。

02050084-bfd4-11ed-bfe3-dac502259ad0.png

通過lock_word.GetState() 獲取鎖狀態,當鎖狀態為unlocked 狀態時,轉換為thin lock 狀態,并通過cas 操作更新lock count。

021aee8a-bfd4-11ed-bfe3-dac502259ad0.png

當鎖狀態為thin lock 狀態時,首先獲取鎖的owner 線程id,如果owner id 與競爭線程id 一致,則有下面兩種情況:

如果lock count加1小于等于(1<<12)-1(4095)時,將lock count+1 更新lock count。

如果lock count加1大于(1<<12)-1時(lock count 區域無法存儲),則調用InflateThinLocked 函數對thin lock 進行膨脹。

Atrace* 相關的函數主要用于systrace 相關信息的打印,trylock 在這里為false。

02331514-bfd4-11ed-bfe3-dac502259ad0.png

當鎖狀態為thin lock 狀態且鎖的owner 線程id 與競爭線程id 不一致,則做一定的等待。

runtime->GetMaxSpinsBeforeThinLockInflation() 的值為50 ,也就是說執行100 次的循環判斷鎖狀態后,再執行50次的sched_yield() 后還未獲得鎖資源,如果還未拿到鎖,則對該鎖進行膨脹。sched_yield() 會主動讓出當前線程的執行權限,并在某個時間后恢復執行。

023da290-bfd4-11ed-bfe3-dac502259ad0.png

當鎖狀態已經是fat lock 狀態,通過lock_word.FatLockMonitor(); 獲取Monitor 對象,并通過Monitor 對象的Lock 函數讓線程進入等待狀態。

024dff50-bfd4-11ed-bfe3-dac502259ad0.png

當鎖狀態已經是hash 狀態時,直接對鎖進行膨脹。

下面看鎖膨脹的過程:

0256bbfe-bfd4-11ed-bfe3-dac502259ad0.png

thin lock 的膨脹有兩種情形:

1).lock count 的值超過了4095,這時鎖的owner 為當前線程,即直接通過Inflate 函數膨脹

2).鎖的owner不是當前線程,通過SuspendThreadByThreadId 暫停鎖的owner 線程(主要是owner 線程和鎖膨脹線程都需要訪問對象的LockWord,避免競態問題),然后通過Inflate 進行膨脹。膨脹完成后再喚醒鎖的owner 線程。

再看Inflate 的過程:

026aeeb2-bfd4-11ed-bfe3-dac502259ad0.png

通過MonitorPool::CreateMonitor函數獲取一個Monitor 的對象m,并通過m->install(self)函數更新對象的LockWord字段,這時LockWord 字段信息包含fat lock 狀態、GC 狀態、MonitorId,然后將m 保存在monitor_list_ 中。

monitor_list_中存儲了當前虛擬機使用的所有Monitor 對象。在GC 的過程中,通過該鏈表,訪問到Monitor 依賴的對象。如果對象變成垃圾對象,則回收該Monitor,否則更新Monitor 依賴的對象信息。

MonitorId 用于唯一標識一個Monitor,生成的方法可以看monitor_pool.h 中的實現。

再看Monitor::Lock的過程:

該函數的實現較長,省去調試相關的代碼。

027fa974-bfd4-11ed-bfe3-dac502259ad0.png

首先介紹Monitor 中最重要的成員monitor_lock_ ,它是Mutex 的實例,通過該實例實現鎖相關的核心邏輯。

TryLock 函數主要是通過Mutex的函數實現一定的自旋等待,并設置鎖的狀態為線程持有的狀態。

monitor_lock_.ExclusiveLock(self);在Mutex 的ExclusiveLock函數中通過futex 系統調用實現了線程的阻塞,futex調用代碼如下。

028f8dbc-bfd4-11ed-bfe3-dac502259ad0.png

4.6 monitor-exit 流程分析

解釋執行和機器碼執行模式都會調用到MonitorExit 函數。

029801e0-bfd4-11ed-bfe3-dac502259ad0.png

通過lock_word.GetState()獲取LockWord 狀態,當狀態為hash 或unlocked 狀態時,通過FailedUnlock函數拋出異常。

02a795c4-bfd4-11ed-bfe3-dac502259ad0.png

當LockWord 的狀態為thin lock 狀態時,有下面兩種情況:

1).鎖的owner 與當前線程不一致,則出錯拋出異常。

2).鎖的owner 與當前線程為同一線程,當鎖有重入時,則將lock count -1,否則設置為unlocked 狀態。

02b45c82-bfd4-11ed-bfe3-dac502259ad0.png

當LockWord 的狀態為fat lock狀態時,獲取該對象關聯的Monitor 對象,并調用Unlock 函數

02c02dd2-bfd4-11ed-bfe3-dac502259ad0.png

在Unlock 函數中lock_count 為0,說明該線程不在持有該鎖,通過SignalWaiterAndReleaseMonitorLock 喚醒阻塞在該鎖上的線程。

五、總結

本文簡單的闡述了對象鎖的使用方式,對象在內存中的結構,并對對象頭中關鍵成員LockWord 進行了分析,最后介紹了synchronized、Object.wait()和Object.notify()在虛擬機中的實現流程。






審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • Android
    +關注

    關注

    12

    文章

    3923

    瀏覽量

    127146
  • JAVA
    +關注

    關注

    19

    文章

    2958

    瀏覽量

    104553
  • JVM
    JVM
    +關注

    關注

    0

    文章

    157

    瀏覽量

    12209
  • 虛擬機
    +關注

    關注

    1

    文章

    908

    瀏覽量

    28095

原文標題:虛擬機中對象鎖實現分析

文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    介紹VirtualBox虛擬機的構建方法

    本系列文章將向大家介紹嵌入式系統開發的各方面知識。本文將向大家介紹VirtualBox虛擬機的構建方法。一、什么是虛擬機二、主流
    發表于 11-08 06:21

    VM虛擬機詳細使用安裝教程

    VM虛擬機詳細使用安裝教程
    發表于 07-30 16:16 ?0次下載

    基于虛擬機技術的DSC仿真系統設計

    提出了基于虛擬機技術的DCS仿真系統的實現方式,描述了虛擬控制器的具體實現方法虛擬機技術的其他
    發表于 12-03 17:26 ?26次下載
    基于<b class='flag-5'>虛擬機</b>技術的DSC仿真系統設計

    基于虛擬機技術的DCS仿真系統設計與實現

    提出了基于虛擬機技術的DCS仿真系統的實現方式,描述了虛擬控制器的具體實現方法虛擬機技術的其他
    發表于 01-16 15:04 ?2155次閱讀
    基于<b class='flag-5'>虛擬機</b>技術的DCS仿真系統設計與<b class='flag-5'>實現</b>

    虛擬機鏡像去冗余方法

    挑戰性的研究熱點.由于虛擬機鏡像之間存在大量重復性的數據塊,高效的去冗余方法對于虛擬機鏡像管理至關重要.然而,傳統的去冗余方法由于需要巨大的資源開銷,會對平臺中托管的
    發表于 01-17 09:50 ?0次下載

    基于硬件虛擬化的虛擬機進程代碼分頁式度量方法

    云環境下惡意軟件可利用多種手段篡改虛擬機( VM)中關鍵業務代碼,威脅其運行的穩定性。傳統的基于主機的度量系統易被繞過或攻擊而失效,針對在虛擬機監視器( VMM)層難以獲取虛擬機中運行
    發表于 03-29 17:40 ?0次下載
    基于硬件<b class='flag-5'>虛擬</b>化的<b class='flag-5'>虛擬機</b>進程代碼分頁式度量<b class='flag-5'>方法</b>

    如何在Win下安裝linux的虛擬機詳細安裝方法資料概述

    本文檔的主要內容詳細介紹的是如何在Win7下安裝linux的虛擬機詳細安裝方法資料概述免費下載。
    發表于 11-28 15:03 ?3次下載

    什么是區塊鏈虛擬機和普通虛擬機有啥區別

    區塊鏈技術領域基礎設施——虛擬機,是實現智能合約系統最為關鍵和核心的技術。智能合約不僅是業務邏輯的載體,同時又扎扎實實地落在了技術實現的層面。由此可見,
    發表于 03-04 10:50 ?4937次閱讀

    linux虛擬機的聯網方法

    虛擬機安裝linux系統無法上網的解決方法
    發表于 05-31 09:27 ?1515次閱讀
    linux<b class='flag-5'>虛擬機</b>的聯網<b class='flag-5'>方法</b>

    虛擬機的設計與實現:C\C++

    虛擬機的設計與實現:C\C++
    發表于 02-21 15:10 ?0次下載

    虛擬機洞察:實現應用感知型基礎架構的關鍵路徑

    電子發燒友網站提供《虛擬機洞察:實現應用感知型基礎架構的關鍵路徑.pdf》資料免費下載
    發表于 08-29 11:07 ?0次下載
    <b class='flag-5'>虛擬機</b>洞察:<b class='flag-5'>實現</b>應用感知型基礎架構的<b class='flag-5'>關鍵</b>路徑

    linux虛擬機怎么運行代碼

    運行代碼是Linux虛擬機中的常見操作,本文將詳細介紹如何運行代碼。 首先,要運行代碼,你需要先安裝好Linux虛擬機,并確保能夠順利運行。接下來,你需要打開
    的頭像 發表于 11-17 10:12 ?4965次閱讀

    Docker與虛擬機的區別

    Docker和虛擬機是兩種不同的虛擬化技術,它們在實現方式、資源消耗、運行性能等方面存在許多差異。本文將會詳細介紹它們的區別。 一、
    的頭像 發表于 11-23 09:37 ?9590次閱讀

    怎么安裝linux虛擬機

    在計算機領域,虛擬機是一種軟件程序,它允許在主操作系統上運行多個虛擬操作系統。Linux虛擬機在開發、測試和學習等環境中得到廣泛應用。本文將詳細介紹
    的頭像 發表于 11-23 10:50 ?1065次閱讀

    虛擬機ubuntu怎么聯網

    與外部網絡通信。本文將詳細介紹虛擬機Ubuntu的網絡連接方法以及一些常見的網絡問題解決辦法。 一、虛擬機網絡概述
    的頭像 發表于 12-27 16:51 ?945次閱讀