乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      JAVA-類加載(類的生命周期)

       印度阿三17 2019-05-22

      類從被加載到虛擬機(jī)內(nèi)存開始,到卸載出內(nèi)存為止。

      解析階段在某些情況下可以在初始化后再開始,這是為了支持 Java 語言的運(yùn)行時(shí)綁定。

      ?

      一、類加載時(shí)機(jī)

      JVM 規(guī)范沒有強(qiáng)制約束類加載過程的第一階段(加載)什么時(shí)候開始,但對于“初始化”階段,有著嚴(yán)格的規(guī)定。

      ?

      1.1.有且僅有 5 種情況必須立即對類進(jìn)行“初始化”:

      1.在遇到 new、putstatic、getstatic、invokestatic 字節(jié)碼指令時(shí),如果類尚未初始化,則需要先觸發(fā)其初始化。
      2.對類進(jìn)行反射調(diào)用時(shí),如果類還沒有初始化,則需要先觸發(fā)其初始化。
      3.初始化一個(gè)類時(shí),如果其父類還沒有初始化,則需要先初始化父類。
      4.虛擬機(jī)啟動時(shí),用于需要指定一個(gè)包含 main() 方法的主類,虛擬機(jī)會先初始化這個(gè)主類。
      5.當(dāng)使用 JDK 1.7 的動態(tài)語言支持時(shí),如果一個(gè) java.lang.invoke.MethodHandle 實(shí)例最后的解析結(jié)果為 REF_getStatic、REF_putStatic、REF_invokeStatic 的方法句柄,并且這個(gè)方法句柄所對應(yīng)的類還沒初始化,則需要先觸發(fā)其初始化。

      這 5 種場景中的行為稱為對一個(gè)類進(jìn)行主動引用,除此之外,其它所有引用類的方式都不會觸發(fā)初始化,稱為被動引用。

      ?

      1.2.幾種被動引用:

      1.通過子類引用父類的靜態(tài)字段,不會導(dǎo)致子類初始化。對于靜態(tài)字段,只有直接定義這個(gè)字段的類才會被初始化。

      class SuperClass {
          static {
              System.out.println("SuperClass init!");
          }
          public static int value = 123;
      }
      
      class SubClass extends SuperClass {
          static {
              System.out.println("SubClass init!");
          }
      }
      
      public class NotInitialization {
          public static void main(String[] args) {
              System.out.println(SubClass.value);
              // SuperClass init!
          }
      }
      View Code

      2.通過數(shù)組定義來引用類,不會觸發(fā)此類的初始化。

      class SuperClass2 {
          static {
              System.out.println("SuperClass init!");
          }
          public static int value = 123;
      }
      
      public class NotInitialization2 {
          public static void main(String[] args) {
              SuperClass2[] superClasses = new SuperClass2[10];
          }
      }
      View Code

      3.常量在編譯階段會存入調(diào)用類的常量池中,本質(zhì)上并沒有直接引用到定義常量的類,因此不會觸發(fā)定義常量的類的初始化。

      class ConstClass {
          static {
              System.out.println("ConstClass init!");
          }
      
          public static final String HELLO_BINGO = "Hello Bingo";
      }
      
      public class NotInitialization3 {
          public static void main(String[] args) {
              System.out.println(ConstClass.HELLO_BINGO);
          }
      }
      View Code

      編譯通過之后,常量存儲到 NotInitialization 類的常量池中,NotInitialization 的 Class 文件中并沒有 ConstClass 類的符號引用入口,這兩個(gè)類在編譯成 Class 之后就沒有任何聯(lián)系了。

      ?

      1.3.關(guān)于接口加載

      當(dāng)一個(gè)類在初始化時(shí),要求其父類全部都已經(jīng)初始化過了,但是一個(gè)接口在初始化時(shí),并不要求其父接口全部都完成了初始化,當(dāng)真正用到父接口的時(shí)候才會初始化。

      ?

      ?

      二、類的加載過程

      ?2.1.加載

      JVM 需要完成 3 件事:

      1.通過類的全限定名獲取該類的二進(jìn)制字節(jié)流。
      2.將二進(jìn)制字節(jié)流所代表的靜態(tài)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)。
      3.在內(nèi)存中創(chuàng)建一個(gè)代表該類的 java.lang.Class 對象,作為方法區(qū)這個(gè)類的各種數(shù)據(jù)的訪問入口。

      怎樣獲取類的二進(jìn)制字節(jié)流,JVM 沒有限制。除了從編譯好的 .class 文件中讀取,還有以下幾種方式:

      從 zip 包中讀取,如 jar、war 等
      從網(wǎng)絡(luò)中獲取
      通過動態(tài)代理生成代理類的二進(jìn)制字節(jié)流
      從數(shù)據(jù)庫中讀取
      。。。

      數(shù)組類本身不通過類加載器創(chuàng)建,由 JVM 直接創(chuàng)建,再由類加載器創(chuàng)建數(shù)組中的元素類。

      加載階段與連接階段的部分內(nèi)容交叉進(jìn)行,但這兩個(gè)階段的開始仍然保持先后順序。

      ?

      2.2.驗(yàn)證

      確保 Class 文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,并且不會危害虛擬機(jī)自身的安全。

      ?

      2.3.準(zhǔn)備

      為類變量(靜態(tài)成員變量)分配內(nèi)存并設(shè)置初始值的階段。這些變量(不包括實(shí)例變量)所使用的內(nèi)存都在方法區(qū)中進(jìn)行分配。

      基本類型初始值(JDK8)https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.12.5

      對于 byte 類型,默認(rèn)值為零,即(byte)0。
      對于 short 類型,默認(rèn)值為零,即(short)0。
      對于 int 類型,默認(rèn)值為零,即 0。
      對于 long 類型,默認(rèn)值為零,即 0L。
      對于 float 類型,默認(rèn)值為正零,即 0.0f。
      對于 double 類型,默認(rèn)值為正零,即 0.0d。
      對于 char 類型,默認(rèn)值為空字符,即 '\u0000'。
      對于 boolean 類型,默認(rèn)值為 false。
      對于所有引用類型,默認(rèn)值為 null。

      存在特殊情況?https://www.jianshu.com/p/520295a63967

      /**
       * 準(zhǔn)備階段過后的初始值為 0 而不是 123,這時(shí)候尚未開始執(zhí)行任何 Java 方法
       */
      public static int value = 123;
      
      /**
       * 同時(shí)使用final 、static來修飾的變量(常量),并且這個(gè)變量的數(shù)據(jù)類型是基本類型或者String類型,就生成ConstantValue屬性來進(jìn)行初始化。
       * 沒有final修飾或者并非基本類型及String類型,則選擇在<clinit>方法中進(jìn)行初始化。
       * 準(zhǔn)備階段虛擬機(jī)會根據(jù) ConstantValue 的設(shè)置將 value 賦值為 123
       */
      public static final int value = 123;

      ?

      2.4.解析

      虛擬機(jī)將常量池內(nèi)的符號引用替換為直接引用。

      https://www.cnblogs.com/shinubi/articles/6116993.html

      符號引用:一個(gè) java 文件會編譯成一個(gè)class文件。在編譯時(shí),java 類并不知道所引用的類的實(shí)際地址,因此只能使用符號引用來代替。

      直接引用:直接指向目標(biāo)的指針(指向方法區(qū),Class 對象)、指向相對偏移量(指向堆區(qū),Class 實(shí)例對象)或指向能間接定位到目標(biāo)的句柄。

      ?

      2.5.初始化

      類加載過程的最后一步,是執(zhí)行類構(gòu)造器 <clinit>() 方法的過程。

      <init>()? 與 <clinit>() 介紹:??https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.9

      https://blog.csdn.net/u013309870/article/details/72975536

      <init>():為 Class 類實(shí)例構(gòu)造器,對非靜態(tài)變量解析初始化,一個(gè)類構(gòu)造器對應(yīng)個(gè)。

      <clinit>():為 Class 類構(gòu)造器對靜態(tài)變量,靜態(tài)代碼塊進(jìn)行初始化,通常一個(gè)類對應(yīng)一個(gè),不帶參數(shù),且是 void 返回。當(dāng)一個(gè)類沒有靜態(tài)語句塊,也沒有對類變量的賦值操作,那么編譯器可以不為這個(gè)類生成 <clinit>() 方法

      加載順序:

      <clinit>() 方法是由編譯器自動收集類中的所有類變量的賦值動作靜態(tài)語句塊(static {} 塊)中的語句合并產(chǎn)生的,編譯器收集的順序是由語句在源文件中出現(xiàn)的順序所決定的。

      靜態(tài)語句塊中只能訪問定義在靜態(tài)語句塊之前的變量,定義在它之后的變量,在前面的靜態(tài)語句塊中可以賦值,但不能訪問。

      static {
          i = 0;  // 給后面的變量賦值,可以正常編譯通過
          System.out.println(i);  // 使用后面的變量,編譯器會提示“非法向前引用”
      }
      static int i = 1;

      虛擬機(jī)會保證在子類的 <clinit>() 方法執(zhí)行之前,父類的 <clinit>() 方法已經(jīng)執(zhí)行完畢。

      由于父類的 <clinit>() 方法先執(zhí)行,意味著父類中定義的靜態(tài)語句塊要優(yōu)先于子類的變量賦值操作。

      static class Parent {
          static {
              A = 2;
          }
          public static int A = 1;
      }
      
      static class Sub extends Parent {
          public static int B = A;
      }
      
      public static void main(String[] args) {
          System.out.println(Sub.B);  // 輸出 1
      }

      來看一個(gè)加載順序的問題

      public static JvmTest jt = new JvmTest();
      
      public static int a;
      public static int b = 0;
      
      static {
          a  ;
          b  ;
      }
      
      public JvmTest() {
          a  ;
          b  ;
      }
      
      public static void main(String[] args) {
          /**
           * 準(zhǔn)備階段:為 jt、a、b 分配內(nèi)存并賦初始值 jt=null、a=0、b=0
           * 解析階段:將 jt 指向內(nèi)存中的地址
           * 初始化:jt 代碼位置在最前面,這時(shí)候 a=1、b=1
           *          a 沒有默認(rèn)值,不執(zhí)行,a還是1,b 有默認(rèn)值,b賦值為0
           *          靜態(tài)塊過后,a=2、b=1
           */
          System.out.println(a);  // 輸出 2
          System.out.println(b);  // 輸出 1
      }

      關(guān)于接口初始化:

      接口中不能使用靜態(tài)代碼塊,但接口也需要通過 <clinit>() 方法為接口中定義的靜態(tài)成員變量顯式初始化。

      接口與類不同,接口的 <clinit>() 方法不需要先執(zhí)行父類的 <clinit>() 方法,只有當(dāng)父接口中定義的變量被使用時(shí),父接口才會初始化。

      ?

      虛擬機(jī)會保證一個(gè)類的 <clinit>() 方法在多線程環(huán)境中被正確加鎖、同步。如果多個(gè)線程同時(shí)去初始化一個(gè)類,那么只會有一個(gè)線程去執(zhí)行這個(gè)類的 <clinit>() 方法。


      https://github.com/doocs/jvm/blob/master/docs/08-load-class-time.md

      https://github.com/doocs/jvm/blob/master/docs/09-load-class-process.md

      來源:http://www./content-1-202551.html

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

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多