echart实例数据 本地加载_JVM 类加载概述
來源:SegmentFault 思否社區
作者:又壞又迷人
JVM簡介
JVM是Java Virtual Machine(Java虛擬機)的縮寫,JVM是一種用于計算設備的規范,它是一個虛構出來的計算機,是通過在實際的計算機上仿真模擬各種計算機功能來實現的。Java虛擬機包括一套字節碼指令集、一組寄存器、一個棧、一個垃圾回收堆和一個存儲方法域。JVM屏蔽了與具體操作系統平臺相關的信息,使Java程序只需生成在Java虛擬機上運行的目標代碼(字節碼),就可以在多種平臺上不加修改地運行。JVM在執行字節碼時,實際上最終還是把字節碼解釋成具體平臺上的機器指令執行。
Java語言的一個非常重要的特點就是與平臺的無關性。而使用Java虛擬機是實現這一特點的關鍵。一般的高級語言如果要在不同的平臺上運行,至少需要編譯成不同的目標代碼。而引入Java語言虛擬機后,Java語言在不同平臺上運行時不需要重新編譯。Java語言使用Java虛擬機屏蔽了與具體平臺相關的信息,使得Java語言編譯程序只需生成在Java虛擬機上運行的目標代碼(字節碼),就可以在多種平臺上不加修改地運行。Java虛擬機在執行字節碼時,把字節碼解釋成具體平臺上的機器指令執行。這就是Java的能夠“一次編譯,到處運行”的原因。
內存結構概述
類加載子系統(Class Loader)
類加載器分為:自定義類加載器 < 系統類加載器 < 擴展類加載器 < 引導類加載器
類加載過程分為:加載、鏈接、驗證、初始化。
程序計數器(Program Counter Register)
是一塊較小的內存空間,可以看作是當前線程所執行字節碼的行號指示器,指向下一個將要執行的指令代碼,由執行引擎來讀取下一條指令。
虛擬機棧 (Stack Area)
棧是線程私有,棧幀是棧的元素。每個方法在執行時都會創建一個棧幀。棧幀中存儲了局部變量表、操作數棧、動態連接和方法出口等信息。每個方法從調用到運行結束的過程,就對應著一個棧幀在棧中壓棧到出棧的過程。
本地方法棧 (Native Method Area)
JVM 中的棧包括 Java 虛擬機棧和本地方法棧,兩者的區別就是,Java 虛擬機棧為 JVM 執行 Java 方法服務,本地方法棧則為 JVM 使用到的 Native 方法服務。
堆 (Heap Area)
堆是Java虛擬機所管理的內存中最大的一塊存儲區域。堆內存被所有線程共享。主要存放使用new關鍵字創建的對象。所有對象實例以及數組都要在堆上分配。垃圾收集器就是根據GC算法,收集堆上對象所占用的內存空間。
Java堆分為年輕代(Young Generation)和老年代(Old Generation);年輕代又分為伊甸園(Eden)和幸存區(Survivor區);幸存區又分為From Survivor空間和 To Survivor空間。
方法區(Method Area)
方法區同 Java 堆一樣是被所有線程共享的區間,用于存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼。更具體的說,靜態變量+常量+類信息(版本、方法、字段等)+運行時常量池存在方法區中。常量池是方法區的一部分。
JDK 8 使用元空間 MetaSpace 代替方法區,元空間并不在JVM中,而是在本地內存中
類加載過程概述
類加載器子系統負責從文件系統或者網絡中在家Class文件,class文件在文件開頭又特定的文件標識。
ClassLoader只負責class文件的加載,至于它是否可以運行,則由ExecutionEngine決定。
加載類的信息存放于一塊被稱為方法區的內存空間。除了類的信息外,方法區中還會存放運行時常量池信息,可能還包括字符串字面量和數字常量(這部分常量信息是Class文件中常量池部分的內存映射)
類加載器ClassLoader角色
類加載過程概述
類的加載過程大致分為三個階段:加載,鏈接,初始化。
類的加載過程一:加載(Loading)
類的加載過程二:鏈接(Linking)
驗證(Verify)
準備(Prepare)
準備階段是進行內存分配。為類變量也就是類中由static修飾的變量分配內存,并且設置初始值,這里要注意,初始值是默認初始值0、null、0.0、false等,而不是代碼中設置的具體值,代碼中設置的值是在初始化階段完成的。另外這里也不包含用final修飾的靜態變量,因為final在編譯的時候就會分配了。這里不會為實例變量分配初始化,類變量會分配在方法區中,而實例對象會隨著對象一起分配到Java堆中。
public class HelloApp { private static int a = 1; // 準備階段為0,而不是1 public static void main(String[] args) { System.out.println(a); }}解析(Resolve)
解析主要是解析字段、接口、方法。主要是將常量池中的符號引用替換為直接引用的過程。直接引用就是直接指向目標的指針、相對偏移量等。
類的加載過程三:初始化(initialization)
需要注意,如果沒有定義靜態變量或靜態代碼塊的話則沒有()
案例如下:
public class HelloApp { static { code = 20; } private static int code = 10; //第一步:在準備階段初始化了code默認值為0。 //第二步:根據類執行順序先執行靜態代碼塊,賦值為20. //第三步:最后賦值為10,輸出結果為10. public static void main(String[] args) { System.out.println(code); // 10 }}通過字節碼文件可以很清楚的看到結果:
0 bipush 20 2 putstatic #3 5 bipush 10 7 putstatic #3 10 return先被賦值為20,然后改為10。
類加載器概述
JVM支持兩種類型的類加載器,分別為引導類加載器(Bootstrap ClassLoader)?和?自定義類加載器(User-Defined ClassLoader)
從概念上講,自定義類加載器一般指的是程序中由開發人員自定義的一類類加載器,但是Java虛擬機是將所有派生于抽象類ClassLoader的類加載器都劃分為自定義類加載器
無論怎么劃分,在程序中最常見的類加載器始終只有三個:
系統類加載器(System Class Loader) < 擴展類加載器(Extension Class Loader) < 引導類加載器(Bootstrap Class Loader)
它們之間的關系不是繼承關系,而是level關系。
系統類加載器和擴展類加載器間接或直接繼承ClassLoader。劃線分為兩大類。
public class HelloApp { public static void main(String[] args) { //獲取系統類加載器 ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2 //獲取其上層:擴展類加載器 ClassLoader extClassLoader = systemClassLoader.getParent(); System.out.println(extClassLoader);//sun.misc.Launcher$ExtClassLoader@60e53b93 //獲取其上層:獲取不到引導類加載器 ClassLoader bootStrapLoader = extClassLoader.getParent(); System.out.println(bootStrapLoader);//null //我們自己定義的類是由什么類加載器加載的:使用系統類加載器 ClassLoader classLoader = HelloApp.class.getClassLoader(); System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2 //看看String是由什么類加載器加載的:使用引導類加載器 ClassLoader classLoaderString = String.class.getClassLoader(); System.out.println(classLoaderString);//null }}引導類加載器(Bootstrap ClassLoader)
擴展類加載器(Extension ClassLoader)
系統類加載器(System ClassLoader)
為什么需要用戶自定義類加載器?
- 隔離加載類
- 修改類加載的方式
- 擴展加載源
- 防止源碼泄露
用戶自定義類加載器實現步驟
關于ClassLoader
ClassLoader是一個抽象類,系統類加載器和擴展類加載器間接或直接繼承ClassLoader。
常用方法如下:
雙親委派機制
Java虛擬機對class文件采用的是按需加載的方式,也就是說需要使用該類的時候才會將它的class文件加載到內存生成class對象。
當某個類加載器需要加載某個.class文件時,它首先把這個任務委托給他的上級類加載器,遞歸這個操作,如果上級的類加載器沒有加載,自己才會去加載這個類。
工作原理
比如我們現在在自己項目中創建一個包名java.lang下創建一個String類。
package java.lang;public class String { static { System.out.println("我是自己創建的String"); }}public class HelloApp { public static void main(String[] args) { String s = new String(); }}執行之后并不會輸出我們的語句,因為我們的String類加載器一開始由系統類加載器一級一級往上委托,最終交給引導類加載器,引導類加載器一看是java.lang包下的,ok,我來執行,最終執行的并不是我們自己創建String類,保證了核心api無法被纂改,避免類的重復加載。
package java.lang;public class String { static { System.out.println("我是自己創建的String"); } public static void main(String[] args) { System.out.println("Hello World !!!"); }}如果我們想運行如上代碼,我們會得到如下錯誤:
錯誤: 在類 java.lang.String 中找不到 main 方法, 請將 main 方法定義為: public static void main(String[] args)否則 JavaFX 應用程序類必須擴展javafx.application.Application因為我們知道在核心api String類中是沒有main方法的,所以我們可以確定加載的并不是我們自己創建的String類。
在JVM中表示兩個Class對象是否為同一個類存在的必要條件:
- 類的完整類名必須一致,包括包名。
- 加載這個類的ClassLoader也必須相同。
順便說一句,我們包名如果為java.lang則會報錯。
-?END -
總結
以上是生活随笔為你收集整理的echart实例数据 本地加载_JVM 类加载概述的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 北京环球影城必须刷脸进入吗
- 下一篇: 如何保证input的输入值不会随着提交