电竞比分网-中国电竞赛事及体育赛事平台

分享

線程安全總結(jié)(二)

 COZUCHOU 2012-09-18
轉(zhuǎn)載自網(wǎng)絡(luò)

現(xiàn)在的計(jì)算機(jī),cpu在計(jì)算的時(shí)候,并不總是從內(nèi)存讀取數(shù)據(jù),它的數(shù)據(jù)讀取順序優(yōu)先級(jí) 是:寄存器-高速緩存-內(nèi)存。線程耗費(fèi)的是CPU,線程計(jì)算的時(shí)候,原始的數(shù)據(jù)來(lái)自內(nèi)存,在計(jì)算過(guò)程中,有些數(shù)據(jù)可能被頻繁讀取,這些數(shù)據(jù)被存儲(chǔ)在寄存器 和高速緩存中,當(dāng)線程計(jì)算完后,這些緩存的數(shù)據(jù)在適當(dāng)?shù)臅r(shí)候應(yīng)該寫回內(nèi)存。當(dāng)個(gè)多個(gè)線程同時(shí)讀寫某個(gè)內(nèi)存數(shù)據(jù)時(shí),就會(huì)產(chǎn)生多線程并發(fā)問(wèn)題,涉及到三個(gè)特 性:原子性,有序性,可見(jiàn)性。在《線程安全總結(jié)》這篇文章中,為了理解方便,我把原子性和有序性統(tǒng)一叫做“多線程執(zhí)行有序性”。支持多線程的平臺(tái)都會(huì)面臨 這種問(wèn)題,運(yùn)行在多線程平臺(tái)上支持多線程的語(yǔ)言應(yīng)該提供解決該問(wèn)題的方案。

 

       那么,我們看看JVM,JVM是一個(gè)虛擬 的計(jì)算機(jī),它也會(huì)面臨多線程并發(fā)問(wèn)題,java程序運(yùn)行在java虛擬機(jī)平臺(tái)上,java程序員不可能直接去控制底層線程對(duì)寄存器高速緩存內(nèi)存之間的同 步,那么java從語(yǔ)法層面,應(yīng)該給開(kāi)發(fā)人員提供一種解決方案,這個(gè)方案就是諸如 synchronized, volatile,鎖機(jī)制(如同步塊,就緒隊(duì) 列,阻塞隊(duì)列)等等。這些方案只是語(yǔ)法層面的,但我們要從本質(zhì)上去理解它,不能僅僅知道一個(gè) synchronized 可以保證同步就完了。   在這里我說(shuō)的是jvm的內(nèi)存模型,是動(dòng)態(tài)的,面向多線程并發(fā)的,沿襲JSL的“working memory”的說(shuō)法,只是不想牽扯到太多底層細(xì)節(jié),因?yàn)?/span> 《線程安全總結(jié)》這篇文章意在說(shuō)明怎樣從語(yǔ)法層面去理解java的線程同步,知道各個(gè)關(guān)鍵字的使用場(chǎng) 景。

 

      今天有人問(wèn)我,那java的線程不是有棧嗎?難道棧不是工作內(nèi)存嗎?工作內(nèi)存這四個(gè)字得放到具體的場(chǎng)景 中描述,方能體現(xiàn)它具體的意義,在描述JVM的線程同步時(shí),工作內(nèi)存指的是寄存器和告訴緩存的抽象描述,具體請(qǐng)自行參閱JLS。上面講的都是動(dòng)態(tài)的內(nèi)存模 型,甚至已經(jīng)超越了JVM的范圍,那么JVM的內(nèi)存靜態(tài)存儲(chǔ)是怎么劃分的?今天還有人問(wèn)我,jvm的內(nèi)存模型不是有eden區(qū)嗎?也不見(jiàn)你提起。我跟他 說(shuō),這是兩個(gè)角度去看的,甚至是兩個(gè)不同的范圍,動(dòng)態(tài)的線程同步的內(nèi)存模型,涵蓋了cpu,寄存器,高速緩存,內(nèi)存;JVM的靜態(tài)內(nèi)存儲(chǔ)模型只是一種對(duì)內(nèi) 存的物理劃分而已,它只局限在內(nèi)存,而且只局限在JVM的內(nèi)存。那些什么線程棧,eden區(qū)都僅僅在JVM內(nèi)存。

 

      說(shuō)說(shuō)JVM的線程棧和有個(gè)朋友反復(fù)跟我糾結(jié)的eden區(qū)吧。JVM的內(nèi)存,被劃分了很多的區(qū)域:

1.程序計(jì)數(shù)器
每一個(gè)Java線程都有一個(gè)程序計(jì)數(shù)器來(lái)用于保存程序執(zhí)行到當(dāng)前方法的哪一個(gè)指令。
2.線程棧
線 程的每個(gè)方法被執(zhí)行的時(shí)候,都會(huì)同時(shí)創(chuàng)建一個(gè)幀(Frame)用于存儲(chǔ)本地變量表、操作棧、動(dòng)態(tài)鏈接、方法出入口等信息。每一個(gè)方法的調(diào)用至完成,就意味 著一個(gè)幀在VM棧中的入棧至出棧的過(guò)程。如果線程請(qǐng)求的棧深度大于虛擬機(jī)所允許的深度,將拋出StackOverflowError異常;如果VM??梢? 動(dòng)態(tài)擴(kuò)展(VM Spec中允許固定長(zhǎng)度的VM棧),當(dāng)擴(kuò)展時(shí)無(wú)法申請(qǐng)到足夠內(nèi)存則拋出OutOfMemoryError異常。
3.本地方法棧
4.堆

每 個(gè)線程的棧都是該線程私有的,堆則是所有線程共享的。當(dāng)我們new一個(gè)對(duì)象時(shí),該對(duì)象就被分配到了堆中。但是堆,并不是一個(gè)簡(jiǎn)單的概念,堆區(qū)又劃分了很多 區(qū)域,為什么堆劃分成這么多區(qū)域,這是為了JVM的內(nèi)存垃圾收集,似乎越扯越遠(yuǎn)了,扯到垃圾收集了,現(xiàn)在的jvm的gc都是按代收集,堆區(qū)大致被分為三大 塊:新生代,舊生代,持久代(虛擬的);新生代又分為eden區(qū),s0區(qū),s1區(qū)。新建一個(gè)對(duì)象時(shí),基本小的對(duì)象,生命周期短的對(duì)象都會(huì)放在新生代的 eden區(qū)中,eden區(qū)滿時(shí),有一個(gè)小范圍的gc(minor gc),整個(gè)新生代滿時(shí),會(huì)有一個(gè)大范圍的gc(major gc),將新生代里的部分對(duì)象轉(zhuǎn)到舊生代里。
5.方法區(qū)
其 實(shí)就是永久代(Permanent Generation),方法區(qū)中存放了每個(gè)Class的結(jié)構(gòu)信息,包括常量池、字段描述、方法描述等等。VM Space描述中對(duì)這個(gè)區(qū)域的限制非常寬松,除了和Java堆一樣不需要連續(xù)的內(nèi)存,也可以選擇固定大小或者可擴(kuò)展外,甚至可以選擇不實(shí)現(xiàn)垃圾收集。相對(duì) 來(lái)說(shuō),垃圾收集行為在這個(gè)區(qū)域是相對(duì)比較少發(fā)生的,但并不是某些描述那樣永久代不會(huì)發(fā)生GC(至 少對(duì)當(dāng)前主流的商業(yè)JVM實(shí)現(xiàn)來(lái)說(shuō)是如此),這里的GC主要是對(duì)常量池的回收和對(duì)類的卸載,雖然回收的“成績(jī)”一般也比較差強(qiáng)人意,尤其是類卸載,條件相 當(dāng)苛刻。
6.常量池
 Class 文件中除了有類的版本、字段、方法、接口等描述等信息外,還有一項(xiàng)信息是常量表(constant_pool table),用于存放編譯期已可知的常量,這部分內(nèi)容將在類加載后進(jìn)入方法區(qū)(永久代)存放。但是Java語(yǔ)言并不要求常量一定只有編譯期預(yù)置入 Class的常量表的內(nèi)容才能進(jìn)入方法區(qū)常量池,運(yùn)行期間也可將新內(nèi)容放入常量池(最典型的String.intern()方法)。

 

關(guān)于垃圾收集,在此不多說(shuō),流到垃圾收集那一章再詳細(xì)說(shuō)吧。關(guān)于java的同 步,其實(shí)還有基于CPU原語(yǔ)的比較并交換的非阻塞算法(CAS),不過(guò)這個(gè)在java的并發(fā)包里已經(jīng)實(shí)現(xiàn)了很多,因此關(guān)于這點(diǎn),就留到j(luò)ava并發(fā)包那一 章介紹吧。后面我會(huì)專門寫一篇文章,JVM內(nèi)存與垃圾收集。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多