深入理解JVM中的類加載機制

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#135ce0","name":"user"}}],"text":"0、引言","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現如今,各種IDE越來越智能,我們程序員的日常開發基本上都是在IDE上完成的,它可以幫助我們將更多的注意力放在實際的業務處理中,隨着這種安逸的編碼生活的持續,我們慢慢也就忘記了代碼運行的底層原理。如果不學習,好像也沒啥問題,畢竟我們的關注重點是代碼邏輯實現上,當出現問題了,百度,谷歌一下,或者問問公司的狠人,問題好像也能愉快的解決,自己好像也理解了似的。但事實上呢,依此週而復始,仍舊不理解,學習一門技術,只有我們真正懂得了其底層原理,才能更好的解決問題。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#135ce0","name":"user"}}],"text":"1、類加載概述","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們在前面幾篇文章中分別講解了","attrs":{}},{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s/of3CbfuAGzMevevwkno60w","title":null},"content":[{"type":"text","text":"類文件結構","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#036aca","name":"user"}}]},{"type":"text","text":",","attrs":{}},{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s/kDnHV0D6aA-tUjK-fVco5w","title":null},"content":[{"type":"text","text":"JVM內存管理","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#036aca","name":"user"}}]},{"type":"text","text":"。這兩篇文章詳細描述了Class文件存儲格式的具體細節及JVM運行時數據區。而今天這篇文章將會講解Class文件中的信息進入到虛擬機中會發生什麼變化。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"**先來個官方敘述:**類加載是Java虛擬機把描述類的數據從Class文件加載到內存,並對數據進行校驗、轉換解析和初始化、最終形成可以被虛擬機直接使用的Java類型。通俗來講,就是我們在完成代碼的編寫後,編譯器會將我們的java文件編譯成對應的class文件(二進制字節碼文件),通過類載器將這些class的時候將其加載到JVM中,生成對應的class對象。下面,讓我們詳細來分析下類加載過程。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#135ce0","name":"user"}}],"text":"2、類加載過程","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於任意一個類,類加載過程可以分爲","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"加載","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"驗證","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"準備","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"解析","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"使用","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"卸載","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"七個階段,如下圖所示:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1a/1afd8cde74443ab0ac42011ae27c10a7.png","alt":"image-20210128214026739","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"圖中的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"加載","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"驗證","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"準備","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"初始化","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"卸載","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"這五個階段的順序是確定的,而","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"解析","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"則不一定,爲了支持java語言的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"運行時綁定特性","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":",解析這個階段可以發生在初始化階段後。接下來我們詳細分析類加載過程中這幾個模塊的作用。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#135ce0","name":"user"}}],"text":"2.1 加載","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"類加載","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"階段是將字節碼文件.Class的二進制數據讀入內存中的方法區中,然後在堆中創建一個","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java.lang.Class對象","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":",對於加載階段的任意一個類都對應着一個Class類型的對象,可以通過","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"getClass()","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"來獲取。對於確定的類Class,無論該類生成多少個對象,其Class類型的對象只有一個,Class類是整個反射的入口。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因此,在類加載階段,Java虛擬機主要完成以下幾類任務:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"通過一個類的全限定類名來獲取定義此類的二進制字節流","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"將這個字節流所代表的的靜態存儲結構轉化爲方法區的的運行時數據結構","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"在內存中生成一個代表這個類的java.lang.Class對象,作爲方法區這個類的各種數據訪問的入口","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#135ce0","name":"user"}}],"text":"2.2驗證","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"驗證是連接階段的第一步,其目的是爲了確保Class文件內的字節流包含的信息符是否符合Java虛擬機規範的要求,保證輸入的字節流不會危害到虛擬機自身的安全。我們也許會有疑問,我們印象中的Java語言是一門相對安全的語言啊(相比較於C++),如單純的使用Java代碼是無法訪問到邊界以外的數據,如果我們非要這麼做,編譯器就會拒絕編譯。但是,回到字節碼層面,一切都變得不可控起來,這是因爲Class文件可以採用很多途徑來產生,並不一定要求用Java源碼編譯出來,如果JVM虛擬機不檢查輸入的字節流,對其完全信任的話,很可能就會因爲載入有害的的字節流導致系統的崩潰。因此,驗證階段在類加載過程中佔有很大的比重,它驗證的項目可以大致分爲以下幾個:文件格式的驗證、元數據驗證、字節碼驗證和符號引用驗證,下面我們一一介紹:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"文件格式驗證","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"文件格式的驗證就是檢查字節流是否符合Class文件格式的規範,不熟悉Class文件格式的可以看我的上一篇文章","attrs":{}},{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s/of3CbfuAGzMevevwkno60w","title":null},"content":[{"type":"text","text":"類文件結構","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#036aca","name":"user"}}]},{"type":"text","text":",文件格式通常檢查一下幾個要素:","attrs":{}}]},{"type":"blockquote","content":[{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":"魔數,是否以0xCAFEBABE開頭","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":"主次版本號是否在合適的範圍","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":"常量池中的常量是否有不被支持的常量類型","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":"指向常量的各種索引值是否有指向不存在的常量或者不符合類型的常量","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":"...........","attrs":{}}]}],"attrs":{}}],"attrs":{}}],"attrs":{}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"元數據驗證","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"元數據的驗證是對字節碼描述的信息進行語義分析,驗證的要素主要包含以下幾點:","attrs":{}}]},{"type":"blockquote","content":[{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":"是否有父類,除了Object外,都有父類","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":"這個類的父類是否繼承被final修飾的類","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":"若這個類不是抽象類,是否實現了父類中的所有方法","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":"...........","attrs":{}}]}],"attrs":{}}],"attrs":{}}],"attrs":{}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"字節碼驗證","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"字節碼驗證是整個驗證過程中的最複雜的一個階段,它主要通過","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"數據流","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"控制流","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"分析,確定程序語義是合法的、符合邏輯的。在第二階段對元數據信息中的數據類型做完校驗後,這個階段將對類的方法體進行校驗分析,保證被校驗類的方法在運行時不會做出危害虛擬機安全的事件,例如:","attrs":{}}]},{"type":"blockquote","content":[{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":"保證任意時刻操作數棧的數據類型與指令代碼序列都能配合工作,例如不會出現類似這樣的情況:在操作棧放置了一個int類型的數據,使用時卻按long類型來加載入本地變量表中。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":"保證跳轉指令不會跳轉到方法體以外的字節碼指令上。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":"保證方法體中的類型轉換是有效地,例如可以把一個子類對象賦值給父類數據類型,這是安全的,但是把父類對象複製給子類數據類型,甚至把對象賦值給與他毫無繼承關係、完全不相干的一個數據類型,則是危險和不合法的。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":".........","attrs":{}}]}],"attrs":{}}],"attrs":{}}],"attrs":{}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"符號引用驗證","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"符號引用驗證可以看做是對類自身(常量池中的各種符號引用)的信息進行","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"匹配性校驗","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":",它的目的是確保解析動作能夠正常執行,如果無法通過符號的引用驗證,則會拋出異常。符號引用驗證階段通常需要校驗以下內容:","attrs":{}}]},{"type":"blockquote","content":[{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":"符號引用中通過字符串描述的全限定名是否能找到對應的類。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":"在制定類中是否存在符合方法的字段描述符以及簡單名稱所描述的方法和字段。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":"符號引用中的類、字段、方法的訪問性(private、protected、public、default)是否可被當前類訪問。","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}}],"text":"......","attrs":{}}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#135ce0","name":"user"}}],"text":"2.3 準備","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"準備階段是正式爲類變量分配內存並設置類變量初始值的階段,這些變量所使用的內存都將在方法區中進行分配。這時候進行內存分配的僅包括","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"類變量(被static修飾的變量)","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":",而不包括實例變量,實例變量將會在對象實例化時隨着對象一起分配在Java堆中。這裏所說的初始值“通常情況”下是數據類型的零值。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#a626a4","name":"user"}}],"text":"public","attrs":{}},{"type":"text","text":" ","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#a626a4","name":"user"}}],"text":"static","attrs":{}},{"type":"text","text":" ","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#a626a4","name":"user"}}],"text":"int","attrs":{}},{"type":"text","text":" number=","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#986801","name":"user"}}],"text":"10","attrs":{}},{"type":"text","text":"  ","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#383a42","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fafafa","name":"user"}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"類變量number在準備階段值是0而不是10,因爲這時候尚未開始執行任何Java方法,而把number賦值爲10的putstatic指令是程序被編譯後,存放於類構造器()方法之中,所以把number賦值爲10的動作將在初始化階段纔會執行。下表列出了所有Java基礎類型的零值:","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#135ce0","name":"user"}}],"text":"2.4 解析","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"解析階段就是將Class中的常量池中的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"符號引用","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"解析爲","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"直接引用","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"。符號引是使用一組符號描述被引用的目標,引用的目標不一定加載到內存中;直接引用可以使直接指向目標地址的指針,相對偏移量或者間接定位到目標的句柄,有了直接引用,引用的目標一定存在在虛擬機中。主要包括四種類型引用的解析,分別是類或接口解析、字段解析、方法解析和接口方法解析。下面以字段解析和方法解析爲例:","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#135ce0","name":"user"}}],"text":"2.5 初始化","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"初始化是類加載過程的最後一步,到了初始化階段,纔開始正真的執行字節碼文件,根據字節碼文件的內容對類的各個字段進行賦值;初始化是執行類構造器()方法的過程。實際上,在連接的準備階段,類變量已賦過一次系統要求的初始值,而在初始化階段,則是根據程序員自己寫的邏輯去初始化類變量和其他資源,舉例如下:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public static int number1 = 5;\n public static int number2 = 6;\n static{\n number = 68;\n }","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在準備階段number1和number2都等於0;在初始化階段number1和number2分別等於5和6。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"總結一下初始化發生的條件:","attrs":{}}]},{"type":"blockquote","content":[{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":"創建一個新的對象實例時(比如new、反射、序列化)","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":"調用一個類型的靜態方法時(即在字節碼中執行invokestatic指令)","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":"調用一個類型或接口的靜態字段,或者對這些靜態字段執行賦值操作時(即在字節碼中,執行getstatic或者putstatic指令),不過用final修飾的靜態字段除外,它被初始化爲一個編譯時常量表達式","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":"調用JavaAPI中的反射方法時(比如調用java.lang.Class中的方法,或者java.lang.reflect包中其他類的方法)","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":"初始化一個類的派生類時(Java虛擬機規範明確要求初始化一個類時,它的超類必須提前完成初始化操作,接口例外)","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":"JVM啓動包含main方法的啓動類時。","attrs":{}}]}],"attrs":{}}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用階段是當執行完初始化後,就可以根據自己的實際需要使用具體的類;當我們在程序中執行System.exit(),加載的類會從內存中卸載,通常情況下,當程序正常執行結束後、或者發生錯誤而終止都會使得已加載的類對象被卸載。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過以上的講解,我們知道了類Class文件被虛擬機加載、使用直至卸載需要經歷的步驟,但是我們忽略了一個非常重要的問題,類是如何被加載器加載的,加載器需要滿足什麼樣的規律?下面我們一一來講解。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#135ce0","name":"user"}}],"text":"3、類加載器與雙親委派模型","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#135ce0","name":"user"}}],"text":"3.1 類加載器","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"類的加載是使用類加載器通過查詢路徑的方式進行的,加載階段既可以使用虛擬機裏內置的引導類加載器來完成,也可以由用戶自定義類加載器來完成Java中的類加載器通常分爲四類:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"啓動類加載器","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"(Bootstrap ClassLoader)、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"擴展類加載器","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"(Extension ClassLoader)、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"應用程序類加載器","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"(Application ClassLoader)、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"用戶自定義類加載器","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"(User ClassLoader)。不同的類加載器負責不同區域的類的加載。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/cc/cc27939b4c7633cdcc0740c55fa12595.png","alt":"image-20210128211514071","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"image-20210128211514071","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"啓動類加載器","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"擴展類加載器","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"應用程序類加載器","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"用戶自定義類加載器","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#135ce0","name":"user"}}],"text":"3.2 雙親委派模型","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面我們講到不同的類加載器都有不同的加載範圍,當某個類加載器要加載某個.class文件時,它首先把這個任務委託給他的上級類加載器,遞歸這個操作,如果上級的類加載器沒有加載,自己纔會去加載這個類。因此,不同類加載器相互配合就形成類雙親委派模型。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ca/ca7d1bd008915129aaeef531071339ab.png","alt":"image-20210128213431950","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"image-20210128213431950","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"我們先分析以下加載流程:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們在上圖可以看到,除了啓動類加載器,每一個類加載器都有一個父類加載器。當一個類加載器加載一個類時,首先會把加載動作委派給他的父加載器,如果父加載器無法完成這個加載動作時才由該類加載器進行加載。由於類加載器會向上傳遞加載請求,所以一個類加載時,首先嚐試加載它的肯定是啓動類加載器(逐級向上傳遞請求,直到啓動類加載器,它沒有父加載器),之後根據是否能加載的結果逐級讓子類加載器嘗試加載,直到加載成功。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"雙親委派模型的作用:","attrs":{}}]},{"type":"blockquote","content":[{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":"防止重複加載同一個","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":".class","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1e6bb8","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong"}],"attrs":{}},{"type":"text","marks":[{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":"。通過委託去向上面問一問,加載過了,就不用再加載一遍。保證數據安全","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":"保證核心","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":".class","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1e6bb8","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong"}],"attrs":{}},{"type":"text","marks":[{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":"不能被篡改。通過委託方式,不會去篡改核心","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":".clas","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1e6bb8","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong"}],"attrs":{}},{"type":"text","marks":[{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":",即使篡改也不會去加載,即使加載也不會是同一個","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":".class","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1e6bb8","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong"}],"attrs":{}},{"type":"text","marks":[{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":"對象了。不同的加載器加載同一個","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":".class","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1e6bb8","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong"}],"attrs":{}},{"type":"text","marks":[{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":"也不是同一個","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Class","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1e6bb8","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong"}],"attrs":{}},{"type":"text","marks":[{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":"對象。這樣保證了","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Class","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1e6bb8","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong"}],"attrs":{}},{"type":"text","marks":[{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}},{"type":"strong","attrs":{}}],"text":"執行安全。","attrs":{}}]}],"attrs":{}}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"參考文獻","attrs":{}},{"type":"text","text":":","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"[1]周志華.深入理解Java虛擬機(第三版)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"[2]https://blog.csdn.net/en_joker/article/details/79959330","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"[3]https://www.cnblogs.com/aspirant/p/7200523.html","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章