<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Ytl's Java Blog

    厚積而薄發(fā)---每一天都是一個全新的開始

    2012年3月1日

         摘要: 歐幾里德算法  閱讀全文

    posted @ 2013-03-21 09:39 ytl 閱讀(313) | 評論 (0)編輯 收藏


    淺談java內(nèi)存模型 

           不同的平臺,內(nèi)存模型是不一樣的,但是jvm的內(nèi)存模型規(guī)范是統(tǒng)一的。其實java的多線程并發(fā)問題最終都會反映在java的內(nèi)存模型上,所謂線程安全無 非是要控制多個線程對某個資源的有序訪問或修改。總結(jié)java的內(nèi)存模型,要解決兩個主要的問題:可見性和有序性。我們都知道計算機有高速緩存的存在,處 理器并不是每次處理數(shù)據(jù)都是取內(nèi)存的。JVM定義了自己的內(nèi)存模型,屏蔽了底層平臺內(nèi)存管理細節(jié),對于java開發(fā)人員,要清楚在jvm內(nèi)存模型的基礎(chǔ) 上,如果解決多線程的可見性和有序性。
           那么,何謂可見性? 多個線程之間是不能互相傳遞數(shù)據(jù)通信的,它們之間的溝通只能通過共享變量來進行。Java內(nèi)存模型(JMM)規(guī)定了jvm有主內(nèi)存,主內(nèi)存是多個線程共享 的。當(dāng)new一個對象的時候,也是被分配在主內(nèi)存中,每個線程都有自己的工作內(nèi)存,工作內(nèi)存存儲了主存的某些對象的副本,當(dāng)然線程的工作內(nèi)存大小是有限制 的。當(dāng)線程操作某個對象時,執(zhí)行順序如下:
     (1) 從主存復(fù)制變量到當(dāng)前工作內(nèi)存 (read and load)
     (2) 執(zhí)行代碼,改變共享變量值 (use and assign)
     (3) 用工作內(nèi)存數(shù)據(jù)刷新主存相關(guān)內(nèi)容 (store and write)

    JVM規(guī)范定義了線程對主存的操作指 令:read,load,use,assign,store,write。當(dāng)一個共享變量在多個線程的工作內(nèi)存中都有副本時,如果一個線程修改了這個共享 變量,那么其他線程應(yīng)該能夠看到這個被修改后的值,這就是多線程的可見性問題。
            那么,什么是有序性呢 ?線程在引用變量時不能直接從主內(nèi)存中引用,如果線程工作內(nèi)存中沒有該變量,則會從主內(nèi)存中拷貝一個副本到工作內(nèi)存中,這個過程為read-load,完 成后線程會引用該副本。當(dāng)同一線程再度引用該字段時,有可能重新從主存中獲取變量副本(read-load-use),也有可能直接引用原來的副本 (use),也就是說 read,load,use順序可以由JVM實現(xiàn)系統(tǒng)決定。
            線程不能直接為主存中中字段賦值,它會將值指定給工作內(nèi)存中的變量副本(assign),完成后這個變量副本會同步到主存儲區(qū)(store- write),至于何時同步過去,根據(jù)JVM實現(xiàn)系統(tǒng)決定.有該字段,則會從主內(nèi)存中將該字段賦值到工作內(nèi)存中,這個過程為read-load,完成后線 程會引用該變量副本,當(dāng)同一線程多次重復(fù)對字段賦值時,比如:
    Java代碼 
    for(int i=0;i<10;i++)   
     a++;  
    線程有可能只對工作內(nèi)存中的副本進行賦值,只到最后一次賦值后才同步到主存儲區(qū),所以assign,store,weite順序可以由JVM實現(xiàn)系統(tǒng)決 定。假設(shè)有一個共享變量x,線程a執(zhí)行x=x+1。從上面的描述中可以知道x=x+1并不是一個原子操作,它的執(zhí)行過程如下:
    1 從主存中讀取變量x副本到工作內(nèi)存
    2 給x加1
    3 將x加1后的值寫回主 存
    如果另外一個線程b執(zhí)行x=x-1,執(zhí)行過程如下:
    1 從主存中讀取變量x副本到工作內(nèi)存
    2 給x減1
    3 將x減1后的值寫回主存 
    那么顯然,最終的x的值是不可靠的。假設(shè)x現(xiàn)在為10,線程a加1,線程b減1,從表面上看,似乎最終x還是為10,但是多線程情況下會有這種情況發(fā)生:
    1:線程a從主存讀取x副本到工作內(nèi)存,工作內(nèi)存中x值為10
    2:線程b從主存讀取x副本到工作內(nèi)存,工作內(nèi)存中x值為10
    3:線程a將工作內(nèi)存中x加1,工作內(nèi)存中x值為11
    4:線程a將x提交主存中,主存中x為11
    5:線程b將工作內(nèi)存中x值減1,工作內(nèi)存中x值為9
    6:線程b將x提交到中主存中,主存中x為

     

    jvm的內(nèi)存模型之eden區(qū)

    所謂線程的“工作內(nèi)存”到底是個什么東西?有的人認為是線程的棧,其實這種理解是不正確的。看看JLS(java語言規(guī)范)對線程工作 內(nèi)存的描述,線程的working memory只是cpu的寄存器和高速緩存的抽象描述

          可能 很多人都覺得莫名其妙,說JVM的內(nèi)存模型,怎么會扯到cpu上去呢?在此,我認為很有必要闡述下,免 得很多人看得不明不白的。先拋開java虛擬機不談,我們都知道,現(xiàn)在的計算機,cpu在計算的時候,并不總是從內(nèi)存讀取數(shù)據(jù),它的數(shù)據(jù)讀取順序優(yōu)先級 是:寄存器-高速緩存-內(nèi)存。線程耗費的是CPU,線程計算的時候,原始的數(shù)據(jù)來自內(nèi)存,在計算過程中,有些數(shù)據(jù)可能被頻繁讀取,這些數(shù)據(jù)被存儲在寄存器 和高速緩存中,當(dāng)線程計算完后,這些緩存的數(shù)據(jù)在適當(dāng)?shù)臅r候應(yīng)該寫回內(nèi)存。當(dāng)個多個線程同時讀寫某個內(nèi)存數(shù)據(jù)時,就會產(chǎn)生多線程并發(fā)問題,涉及到三個特 性:原子性,有序性,可見性。在《線程安全總結(jié)》這篇文章中,為了理解方便,我把原子性和有序性統(tǒng)一叫做“多線程執(zhí)行有序性”。支持多線程的平臺都會面臨 這種問題,運行在多線程平臺上支持多線程的語言應(yīng)該提供解決該問題的方案。

          synchronized, volatile,鎖機制(如同步塊,就緒隊 列,阻塞隊列)等等。這些方案只是語法層面的,但我們要從本質(zhì)上去理解它,不能僅僅知道一個 synchronized 可以保證同步就完了。   在這里我說的是jvm的內(nèi)存模型,是動態(tài)的,面向多線程并發(fā)的,沿襲JSL的“working memory”的說法,只是不想牽扯到太多底層細節(jié),因為 《線程安全總結(jié)》這篇文章意在說明怎樣從語法層面去理解java的線程同步,知道各個關(guān)鍵字的使用場 景。

    說說JVM的eden區(qū)吧。JVM的內(nèi)存,被劃分了很多的區(qū)域:

    1.程序計數(shù)器
    每一個Java線程都有一個程序計數(shù)器來用于保存程序執(zhí)行到當(dāng)前方法的哪一個指令。
    2.線程棧
    線程的每個方法被執(zhí)行的時候,都會同時創(chuàng)建一個幀(Frame)用于存儲本地變量表、操作棧、動態(tài)鏈接、方法出入口等信息。每一個方法的調(diào)用至完成,就意味著一個幀在VM棧中的入棧至出棧的過程。如果線程請求的棧深度大于虛擬機所允許的深度,將拋出StackOverflowError異常;如果VM棧可以動態(tài)擴展(VM Spec中允許固定長度的VM棧),當(dāng)擴展時無法申請到足夠內(nèi)存則拋出OutOfMemoryError異常。
    3.本地方法棧
    4.堆
    每個線程的棧都是該線程私有的,堆則是所有線程共享的。當(dāng)我們new一個對象時,該對象就被分配到了堆中。但是堆,并不是一個簡單的概念,堆區(qū)又劃分了很多區(qū)域,為什么堆劃分成這么多區(qū)域,這是為了JVM的內(nèi)存垃圾收集,似乎越扯越遠了,扯到垃圾收集了,現(xiàn)在的jvm的gc都是按代收集,堆區(qū)大致被分為三大塊:新生代,舊生代,持久代(虛擬的);新生代又分為eden區(qū),s0區(qū),s1區(qū)。新建一個對象時,基本小的對象,生命周期短的對象都會放在新生代的eden區(qū)中,eden區(qū)滿時,有一個小范圍的gc(minor gc),整個新生代滿時,會有一個大范圍的gc(major gc),將新生代里的部分對象轉(zhuǎn)到舊生代里。
    5.方法區(qū) 
    其實就是永久代(Permanent Generation),方法區(qū)中存放了每個Class的結(jié)構(gòu)信息,包括常量池、字段描述、方法描述等等。VM Space描述中對這個區(qū)域的限制非常寬松,除了和Java堆一樣不需要連續(xù)的內(nèi)存,也可以選擇固定大小或者可擴展外,甚至可以選擇不實現(xiàn)垃圾收集。相對來說,垃圾收集行為在這個區(qū)域是相對比較少發(fā)生的,但并不是某些描述那樣永久代不會發(fā)生GC(至 少對當(dāng)前主流的商業(yè)JVM實現(xiàn)來說是如此),這里的GC主要是對常量池的回收和對類的卸載,雖然回收的“成績”一般也比較差強人意,尤其是類卸載,條件相當(dāng)苛刻。
    6.常量池
     Class文件中除了有類的版本、字段、方法、接口等描述等信息外,還有一項信息是常量表(constant_pool table),用于存放編譯期已可知的常量,這部分內(nèi)容將在類加載后進入方法區(qū)(永久代)存放。但是Java語言并不要求常量一定只有編譯期預(yù)置入Class的常量表的內(nèi)容才能進入方法區(qū)常量池,運行期間也可將新內(nèi)容放入常量池(最典型的String.intern()方法)。

    posted @ 2012-03-01 18:12 ytl 閱讀(984) | 評論 (0)編輯 收藏

    主站蜘蛛池模板: 亚洲乱码中文字幕手机在线| 亚洲国产精品免费视频| 99re6在线精品免费观看| 久久精品国产亚洲av麻豆小说| 免费精品国产自产拍在| 深夜a级毛片免费视频| 久久精品视频亚洲| 国产精品免费播放| 日韩免费无码视频一区二区三区| 亚洲第一成人在线| 亚洲综合AV在线在线播放| 日韩欧美一区二区三区免费观看| 国产免费AV片在线观看播放| 亚洲一区二区三区久久久久| 精品亚洲成α人无码成α在线观看 | 67194在线午夜亚洲| 亚洲人成无码网站久久99热国产| 蜜臀98精品国产免费观看| 女人裸身j部免费视频无遮挡| 亚洲国产av一区二区三区丶| 国产福利电影一区二区三区,亚洲国模精品一区 | 国产又黄又爽又刺激的免费网址 | 男男gay做爽爽的视频免费| 亚洲国产精品人久久| 亚洲电影日韩精品| 最近中文字幕免费mv视频7| 黄网站免费在线观看| 免费在线人人电影网| 亚洲日韩乱码中文字幕| 久久亚洲免费视频| 亚洲午夜成人精品电影在线观看| 成年女人男人免费视频播放 | 亚洲熟妇丰满多毛XXXX| 永久免费看bbb| 国产大片线上免费观看| 99视频免费播放| 伊人免费在线观看高清版| 色多多www视频在线观看免费| 国产成人精品日本亚洲专| 亚洲黄色免费在线观看| 亚洲一区二区女搞男|