Ytl's Java Blog

          厚積而薄發---每一天都是一個全新的開始
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

          2011年8月7日

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

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


          淺談java內存模型 

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

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

           

          jvm的內存模型之eden區

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

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

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

          說說JVM的eden區吧。JVM的內存,被劃分了很多的區域:

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

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

          ArrayList
                 關于Java中的transient,volatile和strictfp關鍵字 http://www.iteye.com/topic/52957
                 (1), ArrayList底層使用Object數據實現, private transient Object[] elementData;且在使用不帶參數的方式實例化時,生成數組默認的長度是10。
                (2),  add方法實現
                public boolean add(E e) {
                     //ensureCapacityInternal判斷添加新元素是否需要重新擴大數組的長度,需要則擴否則不
                    ensureCapacityInternal(size + 1);  // 此為JDK7調用的方法 JDK5里面使用的ensureCapacity方法
                    elementData[size++] = e; //把對象插入數組,同時把數組存儲的數據長度size加1
                    return true;
                }
               JDK 7中 ensureCapacityInternal實現
             private void ensureCapacityInternal(int minCapacity) {
                  modCount++;修改次數
                  // overflow-conscious code
                  if (minCapacity - elementData.length > 0)
                      grow(minCapacity);//如果需要擴大數組長度
              }
          /**
               * The maximum size of array to allocate. --申請新數組最大長度
               * Some VMs reserve some header words in an array.
               * Attempts to allocate larger arrays may result in
               * OutOfMemoryError: Requested array size exceeds VM limit  --如果申請的數組占用的內心大于JVM的限制拋出異常
               */
              private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;//為什么減去8看注釋第2行
              /**
               * Increases the capacity to ensure that it can hold at least the
               * number of elements specified by the minimum capacity argument.
               *
               * @param minCapacity the desired minimum capacity
               */
              private void grow(int minCapacity) {
                  // overflow-conscious code
                  int oldCapacity = elementData.length;
                  int newCapacity = oldCapacity + (oldCapacity >> 1); //新申請的長度為old的3/2倍同時使用位移運算更高效,JDK5中: (oldCapacity *3)/2+1
                  if (newCapacity - minCapacity < 0)  
                      newCapacity = minCapacity; 
                  if (newCapacity - MAX_ARRAY_SIZE > 0) //你懂的
                      newCapacity = hugeCapacity(minCapacity);
                  // minCapacity is usually close to size, so this is a win:
                  elementData = Arrays.copyOf(elementData, newCapacity);
              }
           //可以申請的最大長度
              private static int hugeCapacity(int minCapacity) { 
                  if (minCapacity < 0) // overflow
                      throw new OutOfMemoryError();
                  return (minCapacity > MAX_ARRAY_SIZE) ?
                      Integer.MAX_VALUE :
                      MAX_ARRAY_SIZE;
              }



          posted @ 2011-09-24 15:30 ytl| 編輯 收藏

               摘要:   閱讀全文

          posted @ 2011-08-07 11:02 ytl| 編輯 收藏

          主站蜘蛛池模板: 额敏县| 正镶白旗| 顺平县| 延长县| 台东市| 方山县| 宁南县| 简阳市| 仁寿县| 昭觉县| 夏邑县| 辰溪县| 宣城市| 泰来县| 台中市| 罗甸县| 金门县| 磴口县| 长沙县| 扎鲁特旗| 临清市| 灵武市| 临海市| 勐海县| 英超| 抚顺市| 绥芬河市| 白玉县| 贡嘎县| 陕西省| 金坛市| 和龙市| 县级市| 白玉县| 资兴市| 防城港市| 彭泽县| 金湖县| 收藏| 红河县| 宜春市|