《深刻理解Java虛擬機》 Java對象的生命週期

  • Java虛擬機運行時數據區
    • 方法區:存儲 類信息、常量、靜態變量、即便編譯器編譯後的代碼等數據,也有別名叫作非堆。  方法區其中有包含有 運行時常量池,用於存放編譯期生成的各類字面量和符號引用。其中,可經過String.intern()方法將字符串放入運行時常量池中。
    • 堆:存儲的是類實例對象,數組。  JVM 所管理的內存中最大的一塊。Java堆是被全部線程共享的一塊內存區域,在虛擬機啓動時建立。  從內存回收的角度來看,因爲如今收集器基本都採用 分代收集算法,因此堆能夠細分爲 新生代 和老年代;再細分 新生代能夠分爲:Eden空間,From Survivor空間和To Survivor空間等。
    • 虛擬機棧:每一個方法在執行的同時都會建立一個棧幀用於存儲局部變量表、操做數棧、動態連接、方法出口等信息。每一個方法從調用直至執行完成的過程,就對應着一個棧幀在虛擬機棧中入棧和出棧的過程。
    • 本地方法棧: 本地方法棧服務於虛擬機執行Native方法服務。做用與虛擬機棧類似。
    • 程序計數器:程序計數器是一塊較小的內存空間,它能夠看做是當前線程所執行的字節碼的行號指示器,字節碼解釋器工做時就是經過改變這個計數器的值來選取下一條須要執行的字節碼指令,分支、循環、跳轉、異常處理、線程恢復等基礎功能都須要依賴這個計數器來完成。每條線程都須要有一個獨立的程序計數器  
  • Java類加載機制
    • 裝載
      • 加載方式:
        • 從本地系統中直接加載
        • 經過網絡下載class文件
        • 從歸檔文件中加載class文件
        • 從專有數據庫中提取class文件
        • 將Java源文件 動態編譯爲class文件,也就是運行時計算而成
        • 從加密文件中獲取
    • 鏈接
      • 驗證
      •  驗證java版本號,文件格式,
            元數據校驗(是否有父類,是否繼承了final類等java 語法) 
            字節碼驗證(運行檢查,棧數據類型和操做碼操做參數是否吻合)
      • 準備
        •   private static final int a =1;  constantValue 通知虛擬機生成常量賦值,不須要開闢內存。  基於final static 修飾的 基本數據類型和String起做用
      • 解析
        •   將常量池內的符號引用轉變成直接引用
    • 初始化
      • 初始化何時被觸發?    類 主動使用到的時候
                      1 建立類的實例,也就是new
                      2 訪問某個類或者接口的靜態變量,給該靜態變量賦值
                      3 調用類的靜態方法
                      4.反射 (class.forname("..."))
                      5.初始化某個類的子類,則其父類也會被初始化
                      6.java 虛擬機啓動時被標明爲啓動類的類(如springboot啓動類)
    • 使用
    • 卸載
      •   1.該類全部的實例都已經被回收,即java堆中不存在該類的任何實例
             2. 加載該類的classloader  已經被回收
             3. 該類對應的java.lang.Class對象沒有任何地方被引用,沒法在任何地方經過反射訪問該類的方法
  • 類加載器
    • 類加載器的加載特性:
      •  全盤類型機制 
      • 父類委託   
      • 緩存機制    
    • 加載完以後開始使用類,此時須要運行時數據區
      1. PC寄存器
      2. 本地方法棧
      3. 虛擬機棧
      4. 堆:裝載的時候,存儲全部class實例  空間不足拋出oom
      5. 方法區 :
        1. 線程共享區域,class結構信息,運行時常量池,方法,構造器,方法數據,靜態定義
        2. 內存不夠時,拋出OOM
      6. 運行時常量池(在方法區中):包含字符串常量池

    • 動態連接:
       符號引用變成直接引用會改變這個動態連接屬性
  • Java對象內存佈局
    •   Java對象內存分爲三部分: 對象頭,實例數據,對齊填充
    • 對象頭
      •   Mark Word : 哈希碼,分代年齡,線程持有的鎖,偏向鎖ID,偏向時間戳,鎖狀態,還有1bit的佔位符
      •   class Pointer: 指的是類型指針,對象指向它的類元數據的指針,虛擬機經過這個指針來肯定這個對象是哪一個類的實例
    •  實例數據java

      •   對象真正存儲的有效信息,代碼中所定義的各類類型的字段內容。不管是從父類繼承下來的,仍是在子類中定義的,都須要記錄起來。這部分的存儲順序會受到虛擬機分配策略參數和字段在Java遠嗎中定義順序的影響。HotSpot虛擬機默認的分配策略爲longs/doubles、ints、shorts/chars、bytes/booleans、oops,從分配策略中能夠看出,先攻寬度的字段老是被分配到一塊兒的。在知足這個前提條件的狀況下,在父類中定義的變量會出如今子類以前。
    • 對齊填充
      •   HotSpot VM 要求對象大小必須是8字節的整數倍
  • 對象定位方式:
    •   創建對象是爲了使用對象,咱們的Java程序須要經過棧上的reference數據來操做對上的具體對象。目前主流的訪問方式有兩種:使用句柄和直接指針兩種方式
    •  使用句柄訪問的最大好處就是reference中存儲的是穩定的句柄地址,在對象被移動(垃圾回收)時,只會改變句柄中的實例數據指針,而reference自己不修改。算法

    • 使用直接指針訪問方式的好處是速度更快,節省了中間轉發訪問的步驟。
  • Java對象的生命週期
    • 建立階段
    • 應用階段
    • 不可見階段
    • 不可達階段
    • 收集階段
    • 終結階段
    • 空間重分配階段
    • 對象建立過程:
    • 判斷對象是否已經「死」了的算法有兩種: 
      •   引用計數算法: 給對象中添加一個引用計數器,每當有一個地方引用它時,計數器值就加1;當引用失效時,計數器值就減1;任什麼時候刻計數器爲0的狀況,都是不可能再被使用的。
        •   引用計數算法的缺陷就是它很難解決對象之間相互循環引用的問題
      •        可達性分析算法:經過一系列的成爲「GC Roots"的對象做爲起始點,從這些節點開始向下搜索,搜索所走過的路徑成爲引用鏈,當一個對象到GC roots沒有任何引用鏈相連時,則證實此對象是不可用的。
      • 在Java中,可做爲GC roots的對象包括下面幾種:
        •   虛擬機棧(棧幀中的本地變量表)中引用的對象
        •   方法區中類靜態屬性引用的對象
        •        方法區中常量引用的對象
        •       本地方法棧中JNI(即通常說的Native方法)引用的對象。
    • 引用分類:spring

      •  強引用:只要強引用還存在,垃圾收集器永遠不會回收掉被引用的對象 數據庫

      • 軟引用:對於軟引用關聯着的對象,在系統將要發生內存溢出異常以前,將會把這些對象列進回收範圍之中進行第二次回收
      • 弱引用: 被弱引用關聯的對象只能生存到下一次GC發生以前。
      • 虛引用: 爲一個對象設置虛引用關聯的惟一目的就是能在這個對象被收集器回收時收到一個系統通知。
    • 垃圾收集算法
      •   標記-清除算法
        •   不足:
          •   效率問題:標記和清除兩個過程的效率不高
          •        空間問題:標記清除後會產生大量不連續的內存碎片,碎片太多致使之後再程序運行過程當中須要分配較大對象時,沒法找到足夠的連續內存而不得不提早觸發另外一次垃圾收集動做。
      •        複製算法:將可用的內存按容量劃分大小相等的涼快,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活着的對象複製到另外一塊上面,而後再把已使用過的內存空間一次清理掉。這樣使得每次都是對整個半區進行內存回收,內存分配時也就不用考慮內存碎片等複雜狀況,只要移動堆頂指針,按順序分配內存便可,簡單高效。
        •      不足:
          •   當對象存活率較高時,要進行較多的複製操做,效率會下降。
      •   標記-整理算法:標記過程仍然與」標記-清除算法同樣,後續讓全部存活的對象都向一端移動,而後直接清理掉端邊界之外的內存。
      •        分代收集算法:
      • 這種算法並無什麼新的思想,只是根據對象存活週期的不一樣將內存劃分爲幾塊。通常是把Java堆分爲新生代和老年代,這樣就能夠根據各個年代的特色採用最適合的收集算法。在新生代中,每次垃圾收集時都發現有大批對象死去,只有少許存活,那就選用複製算法,只須要付出少許存活對象的複製成本就能夠完成。而老年代中由於對象存活率高、沒有額外空間對他進行分配擔保,就必須使用「標記-清理」或者「標記-整理」算法來進行回收。
相關文章
相關標籤/搜索