文章轉載地址:http://www.cnblogs.com/fanjie/p/6916784.htmlhtml
學完類加載以後,java運行過程就能夠分爲 編譯 》 類加載 》 執行java
類加載主要是由jvm虛擬機負責的,過程很是複雜,類加載分三步 加載 》 鏈接 》初始化,(這裏的加載和本文標題的類加載是不一樣的,標題的類加載包含了完整的三個步驟)下面詳細說說每一步的過程程序員
一、加載:這個很簡單,程序運行以前jvm會把編譯完成的.class二進制文件加載到內存,供程序使用,用到的就是類加載器classLoader ,這裏也能夠看出java程序的運行並非直接依 靠底層的操做系統,而是基於jvm虛擬機。若是沒有類加載器,java文件就只是磁盤中的一個普通文件。數據結構
二、鏈接:鏈接是很重要的一步,過程比較複雜,分爲三步 驗證 》準備 》解析 dom
驗證:確保類加載的正確性。通常狀況由javac編譯的class文件是不會有問題的,可是可能有人的class文件是本身經過其餘方式編譯出來的,這就頗有可能不符合jvm的編 譯規則,這一步就是要過濾掉這部分不合法文件 jvm
準備:爲類的靜態變量分配內存,將其初始化爲默認值 。咱們都知道靜態變量是能夠不用咱們手動賦值的,它天然會有一個初始值 好比int 類型的初始值就是0 ;boolean類型初始值爲false,引用類型的初始值爲null 。 這裏注意,只是爲靜態變量分配內存,此時是沒有對象實例的 操作系統
解析:把類中的符號引用轉化爲直接引用。解釋一下符號引用和直接引用。好比在方法A中使用方法B,A(){B();},這裏的B()就是符號引用,初學java時咱們都是知道這是java的引用,覺得B指向B方法的內存地址,可是這是不完整的,這裏的B只是一個符號引用,它對於方法的調用沒有太多的實際意義,能夠這麼認爲,他就是給程序員看的一個標誌,讓程序員知道,這個方法能夠這麼調用,可是B方法實際調用時是經過一個指針指向B方法的內存地址,這個指針纔是真正負責方法調用,他就是直接引用。線程
三、初始化:爲類的靜態變量賦予正確的初始值,上述的準備階段爲靜態變量賦予的是虛擬機默認的初始值,此處賦予的纔是程序編寫者爲變量分配的真正的初始值指針
如今java程序的執行就能夠分爲htm
類加載的內存分析
類的加載過程咱們已經瞭解,如今來分析一下類加載的內存分配,
類加載究竟是什麼呢?其實類加載不過就是居民虛擬機爲類分配了幾塊內存空間,說的具體一點,就是jvm虛擬機將類的.class文件加載到內存,並將它放到運行時數據區的方法區內,而後在堆區建立一個java.lang.Class對象,用來封裝類在方法區內的數據結構
這裏可能不少人不知道什麼是運行時方法區。咱們簡單瞭解一下jvm虛擬機的內存管理
Java虛擬機在執行Java程序的過程當中會把它所管理的內存劃分爲若干個不一樣的數據區域。這些區域都有各自的用途,以及建立和銷燬的時間,有的區域隨着虛擬機進程的啓動而存在,有些區域則依賴用戶線程的啓動和結束而創建和銷燬。java虛擬機所管理的內存將會包括如下幾個運行時數據區域
不少區域的做用你們可能都不知道,這裏也很少作解釋,咱們只須要知道 其中的兩個
方法區:用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據
堆區:存放對象實例,幾乎全部的對象實例都在這裏分配內存
也就是說,類被加載後,方法區會被分出一塊內存,存儲這個類的全部信息,可是這個內存塊存儲的依然是.class文件,並不能被咱們使用,咱們還須要一個能被直接使用的對象,此時堆區就開始發揮做用。類的信息被存儲在方法區後,jvm虛擬機又會堆區建立一個java.lang.Class對象,這個對象就好像方法區對應類的一個鏡子,把方法區存儲的類的結構所有反射過來,而後封裝起來,成爲了一個Class類的對象(此處運用到反射知識)。這個Class對象與對應的類是一對一服務,由於他有類的結構信息,因此他天然能夠構造出一個類的對象。咱們平時使用的對象就是由這個Class類的對象生成。到此,類的加載已經完成,可是此時依舊沒有咱們須要使用的對象產生(這裏比較繞,說的比較囉嗦)
反正都囉嗦了,在加一個反應內存的圖也很少餘
來個總結
說完了類的加載過程,咱們還要繞回去,談談類爲何會被加載,如何觸發jvm虛擬機加載一個類?
先來一句看不懂的話:全部的Java虛擬機實現必須在每一個類或接口被Java程序「 首次主動使用」時才初始化他們
什麼意思 ?
Java程序對類的使用方式可分爲兩種
– 主動使用
– 被動使用
被動使用之後再講,這裏說說什麼是主動使用,java對類的主動使用有六種狀況
這裏惟一須要解釋的可能就是最後一個,什麼叫被標記爲啓動類的類呢
好比咱們有一個Hello.java文件,可是裏面包含了class Hello ,class Person1 ,class Person2,咱們在控制檯運行的時候會寫java Hello,這個class Hello就是被標記爲啓動類的類。簡單說就是擁有main方法的類
以上的六個活動在第一次發生時,都會促使jvm虛擬機加載類。
除了上述六種狀況之外,其餘狀況都屬於類的被動調用,主動調用和被動調用的區別請看下面代碼
class Test2{
public static final int n = 2;
static{
System.out.println("test");
}
}
public class Test1 {
public static void main(String[] args) {
System.out.println(Test2.n);
}
}
你們思考一下會出現什麼?無論你的答案是什麼,正確答案是2 。 Test2中的靜態代碼塊是沒有運行的,由於Test2並無初始化
注意,這裏的final關鍵字不可缺乏,咱們知道變量被關鍵字fianl修飾以後就不可修改,亦即此變量至關於編譯期常量(是至關於並不是就是常量),常量在java編譯期已經肯定,不須要初始化,可是把fianal去掉,或者把 final int n = 2 改成 final int n = new Random()。,運行的結果將變爲 test ,2 ,由於n的值爲變量或者n值在編譯期不能肯定,就必須通過初始化才能使用n的值