国产精品天干天干,亚洲毛片在线,日韩gay小鲜肉啪啪18禁,女同Gay自慰喷水

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

JVM面試總結(jié)(一)

2022-04-19 22:37 作者:吾之利劍  | 我要投稿

? ???JVM是面試必問的模塊,整個JVM我個人感覺可以分為內(nèi)存模型、類加載機制、GC垃圾回收和性能優(yōu)化四個大塊;

????今天主要總結(jié)一下內(nèi)存模型;

1、Java 是如何實現(xiàn)跨平臺的?

????我們寫的 Java 源碼,編譯后會生成一種 .class 文件,稱為字節(jié)碼文件。Java 虛擬機(JVM)就是負責(zé)將字節(jié)碼文件翻譯成特定平臺下的機器碼然后運行,也就是說,只要在不同平臺上安裝對應(yīng)的 JVM,就可以運行字節(jié)碼文件,運行我們編寫的 Java 程序。

而這個過程,我們編寫的 Java 程序沒有做任何改變,僅僅是通過 JVM 這一 “中間層” ,就能在不同平臺上運行,真正實現(xiàn)了 “一次編譯,到處運行” 的目的。


接下來就會問JVM是什么,由什么組成,怎么運行的?

2、JVM是什么?(談?wù)勀銓VM的理解)

????JVM,即 Java Virtual Machine,Java 虛擬機。它通過模擬一個計算機來達到一個計算機所具有的的計算功能。JVM 能夠跨計算機體系結(jié)構(gòu)來執(zhí)行 Java 字節(jié)碼,主要是由于 JVM 屏蔽了與各個計算機平臺相關(guān)的軟件或者硬件之間的差異,使得與平臺相關(guān)的耦合統(tǒng)一由 JVM 提供者來實現(xiàn)。

3、為什么可以跨平臺?

????1、Java 文件經(jīng)過編譯后生成和平臺無關(guān)的. class 文件。

? ?2、Java虛擬機(JVM)是不跨平臺的,Java工具會把統(tǒng)一的.class文件,加載到對應(yīng)的JVM,又因為該JVM是和這個系統(tǒng)是對應(yīng)的,所以就可以運行。

4、JVM由什么組成的?

JVM 主要由四大部分組成:ClassLoader(類加載器),Runtime Data Area(運行時數(shù)據(jù)區(qū)或者內(nèi)存分區(qū)),Execution Engine(執(zhí)行引擎),Native Interface(本地庫接口);


1、ClassLoader(類加載器):負責(zé)加載字節(jié)碼文件即 class 文件,class 文件在文件開頭有特定的文件標示,并且 ClassLoader 只負責(zé)class 文件的加載,至于它是否可以運行,則由 Execution Engine 決定。

2、Runtime Data Area(運行時數(shù)據(jù)區(qū)或者內(nèi)存分區(qū)):是存放數(shù)據(jù)的,分為五部分:Stack(虛擬機棧),Heap(堆),Method Area(方法區(qū)),PC Register(程序計數(shù)器),Native Method Stack(本地方法棧)。幾乎所有的關(guān)于 Java 內(nèi)存方面的問題,都是集中在這塊。

3、Execution Engine(執(zhí)行引擎):也叫 Interpreter;Class 文件被加載后,會把指令和數(shù)據(jù)信息放入內(nèi)存中,Execution Engine 則負責(zé)把這些命令解釋給操作系統(tǒng),即將 JVM 指令集翻譯為操作系統(tǒng)指令集。

4、Native Interface(本地庫接口):負責(zé)調(diào)用本地接口的。他的作用是調(diào)用不同語言的接口給 JAVA 用,他會在 Native Method Stack 中記錄對應(yīng)的本地方法,然后調(diào)用該方法時就通過 Execution Engine 加載對應(yīng)的本地 lib。

運行時數(shù)據(jù)區(qū)按照Java虛擬機規(guī)定分為以下5個部分:

1、程序計數(shù)器:通過改變計數(shù)器的值,來選取下一條需要執(zhí)行的字節(jié)碼指令。

2、Java虛擬機棧:用于存儲局部變量表、操作數(shù)棧、動態(tài)鏈接、方法出口等信息。

3、本地方法棧:為虛擬機調(diào)用Native方法服務(wù)的。

4、Java堆(線程共享):Java虛擬機中內(nèi)存最大的一塊,是被所有線程共享的,幾乎所有的對象實例都在這里分配內(nèi)存,也是垃圾收集器管理的主要區(qū)域,因此也被稱為GC堆。

5、方法區(qū)(線程共享):用于存儲已被虛擬機加載的類信息、常量、靜態(tài)變量、即時編譯后的代碼等數(shù)據(jù),存在一個叫運行時常量池的區(qū)域,用來存放編譯器生成的各種字面量和符號引用。

5、JVM怎么運行的?(JVM的工作流程)


JVM的啟動過程分為如下四個步驟:

1、JVM的裝入環(huán)境和配置

java.exe負責(zé)查找JRE,并且它會按照如下的順序來選擇JRE:

????自己目錄下的JRE;

????父級目錄下的JRE;

????查注冊中注冊的JRE。

2、裝載JVM

????通過第一步找到JVM的路徑后,Java.exe通過LoadJavaVM來裝入JVM文件。LoadLibrary裝載JVM動態(tài)連接庫,然后把JVM中的到處函數(shù)JNI_CreateJavaVM和JNI_GetDefaultJavaVMIntArgs 掛接到InvocationFunction 變量的CreateJavaVM和GetDafaultJavaVMInitArgs 函數(shù)指針變量上。JVM的裝載工作完成。

3、初始化JVM,獲得本地調(diào)用接口

????調(diào)用InvocationFunction -> CreateJavaVM,也就是JVM中JNI_CreateJavaVM方法獲得JNIEnv結(jié)構(gòu)的實例。

4、運行Java程序

????JVM運行Java程序的方式有兩種:jar包 與 class。

????運行jar 的時候,java.exe調(diào)用GetMainClassName函數(shù),該函數(shù)先獲得JNIEnv實例然后調(diào)用JarFileJNIEnv類中g(shù)etManifest(),從其返回的Manifest對象中取getAttrebutes("Main-Class")的值,即jar 包中文件:META-INF/MANIFEST.MF指定的Main-Class的主類名作為運行的主類。之后main函數(shù)會調(diào)用Java.c中LoadClass方法裝載該主類(使用JNIEnv實例的FindClass)。運行Class的時候,main函數(shù)直接調(diào)用Java.c中的LoadClass方法裝載該類。

6、Java程序是怎么運行的?

Java程序從源文件創(chuàng)建到程序運行要經(jīng)過兩大步驟:

????1、源文件由編譯器編譯成字節(jié)碼(ByteCode);

? ?2、字節(jié)碼由java虛擬機解釋運行。

第一步(編譯): 創(chuàng)建完源文件之后,程序會先被編譯為.class文件。Java編譯一個類時,如果這個類所依賴的類還沒有被編譯,編譯器就會先編譯這個被依賴的類,然后引用,否則直接引用。(如果java編譯器在指定目錄下找不到該類所其依賴的類的.class文件或者.java源文件的話,編譯器話報“cant find symbol”的錯誤。)

? ?編譯后的字節(jié)碼文件格式主要分為兩部分:常量池和方法字節(jié)碼。常量池記錄的是代碼出現(xiàn)過的所有token(類名,成員變量名等等)以及符號引用(方法引用,成員變量引用等等);方法字節(jié)碼放的是類中各個方法的字節(jié)碼。

第二步(運行):Java類運行的過程大概可分為兩個過程:1、類的加載 ?2、類的執(zhí)行。需要說明的是:JVM主要在程序第一次主動使用類的時候,才會去加載該類。也就是說,JVM并不是在一開始就把一個程序就所有的類都加載到內(nèi)存中,而是到不得不用的時候才把它加載進來,而且只加載一次。 ? ? ? ?下面是程序運行的詳細步驟:1、在編譯好Java程序得到MainApp.class文件后,在命令行上敲Java AppMain。系統(tǒng)就會啟動一個JVM進程,JVM進程從classpath路徑中找到一個名為MainApp.class的二進制文件,將MainApp的類信息加載到運行時數(shù)據(jù)區(qū)的方法區(qū)內(nèi),這個過程叫做MainApp類的加載。2、然后JVM找到AppMain的主函數(shù)入口,開始執(zhí)行main函數(shù)。3、main函數(shù)的第一條命令是Animal ?animal = new Animal("Puppy");就是讓JVM創(chuàng)建一個Animal對象,但是這時候方法區(qū)中沒有Animal類的信息,所以JVM馬上加載Animal類,把Animal類的類型信息放到方法區(qū)中。4、加載完Animal類之后,Java虛擬機做的第一件事情就是在堆區(qū)中為一個新的Animal實例分配內(nèi)存, 然后調(diào)用構(gòu)造函數(shù)初始化Animal實例,這個Animal實例持有著指向方法區(qū)的Animal類的類型信息(其中包含有方法表,Java動態(tài)綁定的底層實現(xiàn))的引用。5、當(dāng)使用animal.printName()的時候,JVM根據(jù)animal引用找到Animal對象,然后根據(jù)Animal對象持有的引用定位到方法區(qū)中Animal類的類型信息的方法表,獲得printName()函數(shù)的字節(jié)碼的地址。6、開始運行printName()函數(shù)。

概況來說

????1、寫好的 Java 源代碼文件經(jīng)過 Java 編譯器編譯成字節(jié)碼文件;

????2、通過類加載器加載到內(nèi)存中,被實例化;

????4、然后到 Java 虛擬機中解釋執(zhí)行;

????5、最后通過操作系統(tǒng)操作 CPU 執(zhí)行獲取結(jié)果。

7、 JDK、JRE和JVM之間的關(guān)系

????JDK:Java Development Kit,Java 開發(fā)工具包 - 開發(fā)人員進行 Java 軟件開發(fā)測試的一套工具

????JRE:Java Runtime Environment,Java 運行時環(huán)境 - Java 軟件成品運行所依賴的環(huán)境

????JVM:Java Virtual Machine,Java 虛擬機 - Java 語言實現(xiàn)跨平臺運行的一種軟件

三者的關(guān)系是:JDK包含JRE,JRE包含JVM


8、JVM內(nèi)存分布

1、程序計數(shù)器

程序計數(shù)器:程序計數(shù)器就是臨時記錄方法運行到哪一行了,程序運行實際并不存在并行,而是不同的線程不斷的搶占cpu,然后執(zhí)行一段時間,又重新開始競爭,只是執(zhí)行時間太短,導(dǎo)致人根本感受不到,當(dāng)一個方法運行到某一行的時候開始重新競爭了,就需要記錄下當(dāng)前方法運行到哪了,用來下次cpu被該方法搶占時,從上次中斷的地方繼續(xù)執(zhí)行。

為了線程切換后能恢復(fù)到正確的執(zhí)行位置,每條線程都需要有一個獨立的程序計數(shù)器,各條線程之間計數(shù)器互不影響,獨立存儲,我們稱這類內(nèi)存區(qū)域為“線程私有”的內(nèi)存

2、Java虛擬機棧(Java棧)

線程私有,生命周期和線程,每個方法在執(zhí)行的同時都會創(chuàng)建一個 棧幀用于存儲局部變量表,操作數(shù)棧,動態(tài)鏈接,方法出口等信息。方法的執(zhí)行就對應(yīng)著棧幀在虛擬機棧中入棧和出棧的過程;棧里面存放著各種基本數(shù)據(jù)類型和對象的引用;

3、本地方法棧

Java 中有些代碼的實現(xiàn)是依賴于其他非 Java 語言的(C++),本地方法棧存儲的是維護非 Java 語句執(zhí)行過程中產(chǎn)生的數(shù)據(jù),一般我們認為本地方法棧不會出現(xiàn)內(nèi)存的問題;本地方法棧則是為虛擬機使用到的本地(Native)方法服務(wù)。也是線程私有的。

4、Java堆(堆)

方法區(qū)存放著class信息,而堆中存放了實例化的對象,同一個類的對象可以被實例化多次,對象是可以被其他線程使用的,所以堆也是共享數(shù)據(jù)區(qū)。實例化對象時,對象中有一個對象頭,其中有個類型指針會指向方法區(qū)的類元信息。

Java堆是程序員需要重點關(guān)注的一塊區(qū)域,因為涉及到內(nèi)存的分配(new關(guān)鍵字,反射等)與回收(回收算法,收集器等);

5、方法區(qū)

方法區(qū):也叫永久區(qū),用于存儲已經(jīng)被虛擬機加載的類信息,常量("zdy","123"等),靜態(tài)變量(static變量)等數(shù)據(jù)。 (jdk1.8已經(jīng)將方法區(qū)去掉了,將方法區(qū)移動到直接內(nèi)存)

在 Java8 之后,我們把方法區(qū)稱之為元空間(MetaSpace),方法區(qū)在邏輯上屬于堆

的一部分,但一些具體機制和堆有所區(qū)別,如:一些 JVM 的方法區(qū)是可以不進行垃圾回收

的,關(guān)閉 JVM 時才會釋放方法區(qū)內(nèi)存。所以方法區(qū)還有一個別名叫非堆,目的是和堆分開。

方法區(qū)會存儲類信息、靜態(tài)變量、常量(JDK8 之后不存放字符串常量)、本地機器指 令。

如果加載大量 class 文件,也會造成方法區(qū)內(nèi)存溢出,如一個 tomcat 運行 20~30 個 項目。

6、運行時常量池

運行時常量池是方法區(qū)的一部分,用于存放編譯期生成的各種字面("abc","123"等)和符號引用。

7、 直接內(nèi)存:

直接內(nèi)存:不是虛擬機運行時數(shù)據(jù)區(qū)的一部分,也不是java虛擬機規(guī)范中定義的內(nèi)存區(qū)域;

????1、如果使用了NIO,這塊區(qū)域會被頻繁使用,在Java堆內(nèi)可以用directByteBuffer對象直接引用并操作;

????2、這塊內(nèi)存不受Java堆大小限制,但受本機總內(nèi)存的限制,可以通過MaxDirectMemorySize來設(shè)置(默認與堆內(nèi)存最大值一樣),所以也會出現(xiàn)OOM異常;

9、擴展

一般重點會問你,類放在哪,常量,變量的位置,線程共享的內(nèi)存和獨有的內(nèi)存;

Java中new處的對象存放在Java堆中,而對象的引用存放在虛擬機棧中。

Java中的Class也是一個類,所以Class對象也存放在堆當(dāng)中,存放在方法區(qū)當(dāng)中的是類的元數(shù)據(jù),即類加載器從class文件中提取出來的類型信息、方法信息、字段信息等。

jdk1.7靜態(tài)變量存放在方法區(qū)中

jdk1.8以后類型信息,字段,方法,常量,保存在本地內(nèi)存的元空間(方法區(qū)),但字符串常量池,靜態(tài)變量仍在堆,new申請的內(nèi)存是在堆中。


1、堆和棧功能上的區(qū)別:

????以棧幀的方式存儲方法調(diào)用的過程,并存儲方法調(diào)用過程中基本數(shù)據(jù)類型的變量(int、short、long、byte、float、double、boolean、char等)以及對象的引用變量,其內(nèi)存分配在棧上,變量出了作用域就會自動釋放;

????而堆內(nèi)存用來存儲Java中的對象。無論是成員變量,局部變量,還是類變量,它們指向的對象都存儲在堆內(nèi)存中;

2、堆和棧在線程共享和線程私有區(qū)別

????棧內(nèi)存歸屬于單個線程,每個線程都會有一個棧內(nèi)存,其存儲的變量只能在其所屬線程中可見,即棧內(nèi)存可以理解成線程的私有內(nèi)存。

堆內(nèi)存中的對象對所有線程可見。堆內(nèi)存中的對象可以被所有線程訪問。

?????棧的內(nèi)存要遠遠小于堆內(nèi)存,棧的深度是有限制的,如果遞歸沒有及時跳出,很可能發(fā)生StackOverFlowError問題。

????可以通過-Xss選項設(shè)置棧內(nèi)存的大小( 這個參數(shù)是設(shè)定單個線程的??臻g)。-Xms選項可以設(shè)置堆的開始時的大小,-Xmx選項可以設(shè)置堆的最大值。



以上內(nèi)容僅供參考,請合理利用搜索引擎!



JVM面試總結(jié)(一)的評論 (共 條)

分享到微博請遵守國家法律
江永县| 伽师县| 河池市| 来凤县| 西平县| 新蔡县| 横山县| 关岭| 扎囊县| 墨江| 通化县| 漯河市| 泸州市| 南汇区| 西安市| 蚌埠市| 佛冈县| 涞水县| 和静县| 当雄县| 文登市| 乳山市| 乾安县| 江源县| 天门市| 鄂温| 城口县| 武强县| 乌鲁木齐县| 北碚区| 佛山市| 班玛县| 白沙| 兴海县| 台北市| 高阳县| 什邡市| 乐至县| 枝江市| 外汇| 新邵县|