jvm指的是Java虛擬機,一種能夠運行Java字節碼的虛擬機。它能夠模擬具有完整硬件系統功能,運行在一個完全隔離環境中的完整計算機系統。作為一種編程語言的虛擬機,它不只是專注于Java語言,只要生成編譯...
jvm指的是Java虛擬機,一種能夠運行Java字節碼的虛擬機。它能夠模擬具有完整硬件系統功能,運行在一個完全隔離環境中的完整計算機系統。作為一種編程語言的虛擬機,它不只是專注于Java語言,只要生成編譯文件匹配jvm對加載編譯文件格式的要求,任何語言都可以由jvm編譯運行。比如scala,kotlin等。
一,JVM的基本組成結構。
jvm由三部分組成:
1)類加載子系統
2)運行時數據區(內存空間,內存結構)
3)執行引擎
結構圖如下:
點擊添加圖片描述(最多60個字)
深入了解運行時數據區:
線程共享:
①方法區(1.8之后已經不叫方法區了,叫元數據區):用來存儲被jvm加載的類信息,常量,靜態變量等數據。也就是類的所有屬性,方法以及構造器,接口代碼都在這里定義。
這個區域包含一個很重要的區域叫常量池。class對象除了類的版本,屬性,方法等信息外,還有一些常量以及字面值,而這些幾乎都存在于常量池里,這部分內容將在被加載的過程中存放到常量池里。
②堆:實例化的對象以及數組都存在這個區域。這個區域的cg最活躍,也就是cg進行垃圾回收的重要場所。這個區域,cg是以分代回收算法進行垃圾回收的。從cg的角度看,堆又可以分為:新生代和老年代。
線程私有:
③程序計數器:也就是一個指針,指向方法區中的方法字節碼。也就是用于存儲每條線程的執行字節碼指令地址。每條線程都會擁有自己的程序計數器,以便線程切換的時候,能使程序正常運行。
④jvm棧:存儲著每一個棧幀。每個方法的執行都對應著每一個棧幀,棧幀里放著局部變量表,操作數棧,動態連接,方法出口等信息。每個方法的執行以及結束都對應到棧幀的入棧與出棧。
⑤本地方法棧:這個跟jvm棧差不多,只是jvm棧為我們的java服務的,而本地方法棧是為native 方法服務。
深入了解類加載子系統:
我們的Java文件被jvm編譯器編譯成class對象之后,在runtime時,由類加載子系統將class字節碼文件加載到運行時數據區中。
但是,類加載子系統在類的整個生命周期中主要的工作是什么呢?
類的生命周期:
類的生命周期分為:
加載-->連接-->初始化-->使用-->卸載
而連接過程又分為:驗證-->準備-->解析。其實這個過程就是類加載子系統工作的過程。
一,工作過程
1,加載:將class字節碼文件加載到運行時數據區中。
2.1,驗證:1,檢查字節碼文件是否正確。2,檢查文件頭的magic number是否正確。
何為magic number?我們用編輯器打開隨意一個class文件,發現都是以CAFEBABE開頭。把這數據改掉或者刪除,都會出錯。這個就是magic number
2.2,準備:給類的靜態變量分配內存,賦予初始值。這里的初始值并不是賦值的值。打個比方:private int i = 100; 在準備階段,并不是將100賦值給i。而是給i賦值0,因為int的默認初始值是0。
2.3)解析:檢查指定的類是否引用了其他的類或者接口,是否能正常引入。也就是裝載當前類引入依賴的其他類。
3)初始化:這里才是給類的靜態變量賦正確的初始值。i的值由0變為100。
使用,卸載這就沒什么可說的了。
通過源碼了解加載過程:
在rt.jar包中的java.lang.ClassLoader類中,我們可以查看類加載實現過程的代碼,具體源碼如下:
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
源碼的注釋寫得很清楚了:
1)加載的時候使用了synchronized加鎖機制,也就是當前線程有在加載當前這個類時,不允許其他類進行加載。
2)判斷這個類是否已經被加載,如果已經加載了,就不再加載。如果沒有,則要加載。
我們在看源碼時,看到
//從parent這個成員變量中,我們得知
private final ClassLoader parent;
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
}
這是一個很重要的信息~由此引申一個很重要的概念
二,雙親委派機制
我們先了解類加載器的種類:
1,啟動類加載器Bootstrap ClassLoader:負責將java_home/lib下的類加載到內存中
2,擴展類加載器Extension ClassLoader:負責將java_home/lib/extde的類加載到內存中
3,系統類加載器Application ClassLoader:負責將classpath的類加載到內存中
4,用戶自定義加載器User ClassLoader:負責加載用戶自定義路徑下的類包
在看源碼可以看出,每次加載類的時候,當前加載器都將該類一層一層交給parent,而且每次都檢查是否已經加載了,如果加載了就不再加載。當交給parent時,如果parent能加載,則就加載。如果parent都不能加載,則由當前加載器進行加載。
雙親委派的優勢:
沙箱安全機制:比如自己寫的String.class類不會被加載,這樣可以防止核心庫被隨意篡改
避免類的重復加載:當父ClassLoader已經加載了該類的時候,就不需要子CJlassLoader再加載一次
以上就是jvm的內存結構以及類加載子系統的相關內容。jvm還有許許多多的知識,比如垃圾回收機制,調優等,以后我們慢慢了解
來源:本文內容搜集或轉自各大網絡平臺,并已注明來源、出處,如果轉載侵犯您的版權或非授權發布,請聯系小編,我們會及時審核處理。
聲明:江蘇教育黃頁對文中觀點保持中立,對所包含內容的準確性、可靠性或者完整性不提供任何明示或暗示的保證,不對文章觀點負責,僅作分享之用,文章版權及插圖屬于原作者。
Copyright?2013-2024 JSedu114 All Rights Reserved. 江蘇教育信息綜合發布查詢平臺保留所有權利
蘇公網安備32010402000125
蘇ICP備14051488號-3技術支持:南京博盛藍睿網絡科技有限公司
南京思必達教育科技有限公司版權所有 百度統計