柚子快報(bào)邀請(qǐng)碼778899分享:JVM(java虛擬機(jī))
柚子快報(bào)邀請(qǐng)碼778899分享:JVM(java虛擬機(jī))
一、什么是JVM
????????Java虛擬機(jī)(Java Virtual Machine 簡(jiǎn)稱JVM)是運(yùn)行所有Java程序的抽象計(jì)算機(jī),是Java語(yǔ)言的運(yùn)行環(huán)境。
????????虛擬機(jī)是一種抽象化的計(jì)算機(jī),通過(guò)在實(shí)際的計(jì)算機(jī)上仿真模擬各種計(jì)算機(jī)功能來(lái)實(shí)現(xiàn)的。Java虛擬機(jī)有自己完善的硬體架構(gòu),如處理器、堆棧、寄存器等,還具有相應(yīng)的指令系統(tǒng)。Java虛擬機(jī)屏蔽了與具體操作系統(tǒng)平臺(tái)相關(guān)的信息,使得Java程序只需生成在Java虛擬機(jī)上運(yùn)行的目標(biāo)代碼(字節(jié)碼),就可以在多種平臺(tái)上不加修改地運(yùn)行。
????????目前 HotSpot 虛擬機(jī)占用絕對(duì)的市場(chǎng)地位,不管是現(xiàn)在仍在廣泛使用 JDK6 ,還是使用比較多的 JDK8 中,默認(rèn)的虛擬機(jī)都是 HotSpot ;HotSpot 是 Sun/Oracle JDK 和 OpenJDK 的默認(rèn)虛擬機(jī)。從服務(wù)器、桌面到移動(dòng)端、嵌入式都有應(yīng)用。名稱中的 HotSpot 指的就是它的熱點(diǎn)代碼探測(cè)技術(shù)。它能通過(guò)計(jì)數(shù)器找到最具編譯價(jià)值的代碼,觸發(fā)即時(shí)編譯(JIT) 或棧上替換;通過(guò)編譯器與解釋器協(xié)同工作,在最優(yōu)化的程序響應(yīng)時(shí)間與最佳執(zhí)行性能中取得平衡。
二、JVM運(yùn)行流程
?
1、JVM執(zhí)行流程
????????程序在執(zhí)行之前先要把java代碼轉(zhuǎn)換成字節(jié)碼(class文件),JVM 首先需要把字節(jié)碼通過(guò)一定的方式 類加載器(ClassLoader) 把文件加載到內(nèi)存中 運(yùn)行時(shí)數(shù)據(jù)區(qū)(Runtime Data Area) ,而字節(jié)碼文件是 JVM 的一套指令集規(guī)范,并不能直接交給底層操作系統(tǒng)去執(zhí)行,因此需要特定的命令解析器 執(zhí)行引擎(Execution Engine)將字節(jié)碼翻譯成底層系統(tǒng)指令再交由CPU去執(zhí)行,而這個(gè)過(guò)程中需要調(diào)用其他語(yǔ)言的接口 本地庫(kù)接口(Native Interface) 來(lái)實(shí)現(xiàn)整個(gè)程序的功能,這就是這4個(gè)主要組成部 分的職責(zé)與功能。
2、JVM內(nèi)存模型
1、方法區(qū)
????????方法區(qū)的作用:用來(lái)存儲(chǔ)被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)的。在《 Java 虛擬機(jī)規(guī)范中》把此區(qū)域稱之為 “ 方法區(qū) ” ,而在 HotSpot 虛擬機(jī)的實(shí)現(xiàn)中,在 JDK 7 時(shí)此區(qū)域叫做永久代(PermGen ), JDK 8 中叫做元空間( Metaspace )。 PS :永久代( PermGen )和元空間( Metaspace )是 HotSpot 中對(duì)《 Java 虛擬機(jī)規(guī)范》中方法區(qū)的實(shí)現(xiàn)。
2、棧區(qū)
????????Java 虛擬機(jī)棧的作用: Java 虛擬機(jī)棧的生命周期和線程相同, Java 虛擬機(jī)棧描述的是 Java 方法執(zhí)行的內(nèi)存模型:每個(gè)方法在執(zhí)行的同時(shí)都會(huì)創(chuàng)建一個(gè)棧幀(Stack Frame )用于存儲(chǔ)局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、方法出口等信息。咱們常說(shuō)的堆內(nèi)存、棧內(nèi)存中,棧內(nèi)存指的就是虛擬機(jī)棧。
Java 虛擬機(jī)棧主要包括:
????????1. 局部變量表: 存放了編譯器可知的各種基本數(shù)據(jù)類型 (8 大基本數(shù)據(jù)類型 ) 、對(duì)象引用。局部變量表所需的內(nèi)存空間在編譯期間完成分配,當(dāng)進(jìn)入一個(gè)方法時(shí),這個(gè)方法需要在幀中分配多大的局部變量空間是完全確定的,在執(zhí)行期間不會(huì)改變局部變量表大小。簡(jiǎn)單來(lái)說(shuō)就是存放方法參數(shù)和局部變量。 ????????2. 操作棧:每個(gè)方法會(huì)生成一個(gè)先進(jìn)后出的操作棧。 ????????3. 動(dòng)態(tài)鏈接:指向運(yùn)行時(shí)常量池的方法引用。 ????????4. 方法返回地址: PC 寄存器的地址。
3、本地方法棧:
????????本地方法棧和虛擬機(jī)棧類似,只不過(guò) Java?虛擬機(jī)棧是給?JVM?使用的,而本地方法棧是給本地方法使用的。
4、堆:? ??
????????堆的作用:程序中創(chuàng)建的所有對(duì)象都在保存在堆中。?我們常見(jiàn)的 JVM 參數(shù)設(shè)置 -Xms10m 最小啟動(dòng)內(nèi)存是針對(duì)堆的, -Xmx10m 最大運(yùn)行內(nèi)存也是針對(duì)堆的。ms 是 memory start 簡(jiǎn)稱, mx 是 memory max 的簡(jiǎn)稱。堆里面分為兩個(gè)區(qū)域:新生代和老生代,新生代放新建的對(duì)象,當(dāng)經(jīng)過(guò)一定 GC 次數(shù)之后還存活的對(duì)象會(huì)放入老生代。新生代還有 3 個(gè)區(qū)域:一個(gè) Endn + 兩個(gè) Survivor ( form/to?)。
5、程序計(jì)數(shù)器:
????????程序計(jì)數(shù)器的作用:用來(lái)記錄當(dāng)前線程執(zhí)行的行號(hào)的。程序計(jì)數(shù)器是一塊比較小的內(nèi)存空間,可以看做是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器。如果當(dāng)前線程正在執(zhí)行的是一個(gè) Java 方法,這個(gè)計(jì)數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址; 如果正在執(zhí)行的是一個(gè)Native 方法,這個(gè)計(jì)數(shù)器值為空。程序計(jì)數(shù)器內(nèi)存區(qū)域是唯一一個(gè)在 JVM 規(guī)范中沒(méi)有規(guī)定任何 OOM 情況的區(qū)域!
3、雙親委派機(jī)制
1、什么是雙親委派機(jī)制
????????雙親委派機(jī)制(Parent-Delegate Model)是Java類加載器中采用的一種類加載策略。該機(jī)制的核心思想是:如果一個(gè)類加載器收到了類加載請(qǐng)求,默認(rèn)先將該請(qǐng)求委托給其父類加載器處理。只有當(dāng)父級(jí)加載器無(wú)法加載該類時(shí),才會(huì)嘗試自行加載。
??
2、java中的類加載器主要分為
?? ? ? ? 用戶自定義類加載器
? ? ? ? 應(yīng)用程序類加載器(Application ClassLoader)
? ? ? ? 擴(kuò)展類加載器(Extention ClassLoader)
? ? ? ? 啟動(dòng)類加載器(BootStrap ClassLoader)?
3、為什么需要雙親委派機(jī)制
? ? ? ? 1、通過(guò)雙親委派機(jī)制,可以避免類的重復(fù)加載,當(dāng)父加載器已經(jīng)加載過(guò)某一個(gè)類時(shí),子加載器就不會(huì)再重新加載這個(gè)類。
??2. 通過(guò)雙親委派機(jī)制,可以保證安全性。因?yàn)锽ootstrapClassLoader在加載的時(shí)候,只會(huì)加載JAVA_HOME中的jar包里面的類,如java.lang.String,那么這個(gè)類是不會(huì)被隨意替換的。那么,就可以避免有人自定義一個(gè)有破壞功能的java.lang.String被加載。這樣可以有效的防止核心Java API被篡改。
4、GC常用的算法
????????垃圾回收機(jī)制是由垃圾回收器Garbage Collection來(lái)實(shí)現(xiàn)的。GC是后臺(tái)的守護(hù)進(jìn)程,它的特別之處是它是一個(gè)低優(yōu)先級(jí)進(jìn)程。但是可以根據(jù)內(nèi)存的使用情況動(dòng)態(tài)的調(diào)整他的優(yōu)先級(jí),因此,它是內(nèi)存中低到一定程度時(shí),才會(huì)自動(dòng)運(yùn)行,從而實(shí)現(xiàn)對(duì)內(nèi)存的回收,這就是垃圾回收的時(shí)間不確定的原因。這個(gè)服務(wù)不是我們啟動(dòng)的是自動(dòng)啟動(dòng)的。
1、分代收集算法
? ? ? ? 這種算法并沒(méi)有什么新的思路,只是根據(jù)對(duì)象的存活周期的不同,將內(nèi)存劃分為幾塊。一般是把java堆分成新生代和老年代,這樣就可以根絕各個(gè)年代的特點(diǎn)采取最適當(dāng)?shù)氖占惴?。在新生代中,每次垃圾回收時(shí)都發(fā)現(xiàn)大批對(duì)象的死去,只有少量存活,那就選用復(fù)制算法,只需要付出少量存活對(duì)象的復(fù)制成本就可以完成收集。而老年代中因?yàn)閷?duì)象存活率高,沒(méi)有額外空間對(duì)它進(jìn)行分配擔(dān)保,就必須使用”標(biāo)記-清理”或者“標(biāo)記-整理”算法來(lái)進(jìn)行回收。
2、復(fù)制算法?
? ? ? ? java新建的對(duì)象存儲(chǔ)在新生代的伊甸區(qū),第一次GC會(huì)將幸存的對(duì)象復(fù)制進(jìn)to幸存區(qū),同時(shí)會(huì)將form幸存區(qū)里存活的對(duì)象拷貝進(jìn)to幸存區(qū),此時(shí)伊甸區(qū)和原來(lái)的form幸存區(qū)都為空,存活的對(duì)象都在原來(lái)的to幸存區(qū)里(新的form區(qū)),當(dāng)一個(gè)對(duì)象經(jīng)過(guò)指定次數(shù)(15次)的GC后仍然存活,那么此時(shí)他就會(huì)被存入老年代區(qū)。這樣使得每次都是對(duì)整個(gè)半?yún)^(qū)進(jìn)行內(nèi)存回收,內(nèi)存分配時(shí)也就不用考慮內(nèi)存碎片等復(fù)雜情況。只要移動(dòng)堆指針,按照順序分配內(nèi)存即可,實(shí)現(xiàn)簡(jiǎn)單,運(yùn)行高效。只是這種算法的代價(jià)是將內(nèi)存縮小為原來(lái)的一半。
3、引用計(jì)數(shù)器法
????????????????原理:此對(duì)象有一個(gè)引用,既增加一個(gè)計(jì)數(shù)器,刪除一個(gè)引用減少一個(gè)計(jì)數(shù)器,垃圾回收時(shí),只回收計(jì)數(shù)器為0的對(duì)象,此算法最致命的是無(wú)法處理循環(huán)引用的情況。簡(jiǎn)單但是速度很慢,缺陷是不能處理循環(huán)引用的情況。?
4、標(biāo)記清除算法
????????標(biāo)記清除算法分為“標(biāo)記”和“清除”兩個(gè)階段:首先標(biāo)記出所有需要回收的對(duì)象,在標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對(duì)象。之所以說(shuō)他是最基礎(chǔ)的收集算法,是因?yàn)楹罄m(xù)的收集算法都是基于這種思路并且對(duì)其不足進(jìn)行改進(jìn)而得到的。
它的主要不足有兩個(gè):? 1. 一個(gè)是效率問(wèn)題,標(biāo)記和清除兩個(gè)過(guò)程的效率都不高? 2. 另一個(gè)是空間問(wèn)題,標(biāo)記清除后會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片,空間碎片太多可能會(huì)導(dǎo)致以后再程序運(yùn)行過(guò)程中需要分配較大對(duì)象時(shí),無(wú)法找到足夠的連續(xù)的內(nèi)存而不得不提前觸發(fā)另一次垃圾收集動(dòng)作。
5、標(biāo)記壓縮算法
????????復(fù)制收集算法在對(duì)象存活率較高的時(shí)候,就要進(jìn)行較多的復(fù)制操作,效率將會(huì)變低。更關(guān)鍵的是,如果不想浪費(fèi)50%的空間,就需要額外的空間進(jìn)行分配擔(dān)保,以應(yīng)對(duì)被使用的內(nèi)存中所有對(duì)象都是100%存活的極端情況,所以在老年代一般不能直接選用這種算法。根據(jù)老年代的特點(diǎn)。有人提出了另外一種“標(biāo)記-整理”(Mark-Compact)算法,標(biāo)記過(guò)程仍然與”標(biāo)記-清除”算法一樣,但是后續(xù)步驟不是直接對(duì)可回收對(duì)象進(jìn)行清理,而是讓所有存活的對(duì)象都向一端移動(dòng),然后直接清理掉端邊界意外的內(nèi)存。
三、JVM調(diào)優(yōu)
1、什么是OOM
OOM是OutOfMemoryError的意思,即內(nèi)存溢出。
當(dāng)程序需要的內(nèi)存空間大于系統(tǒng)為其分配的內(nèi)存空間時(shí),就會(huì)發(fā)生內(nèi)存溢出。這種情況下的后果是項(xiàng)目程序崩潰。內(nèi)存溢出是程序運(yùn)行過(guò)程中常見(jiàn)的問(wèn)題,可能導(dǎo)致程序無(wú)法正常運(yùn)行或達(dá)到預(yù)期的性能。
導(dǎo)致OutOfMemoryError的原因有多種,包括但不限于:
內(nèi)存中加載的數(shù)據(jù)量過(guò)于龐大,如一次從數(shù)據(jù)庫(kù)取出過(guò)多數(shù)據(jù)。集合中的對(duì)象引用在使用完后未清空,使得JVM不能回收。代碼中存在死循環(huán)或循環(huán)產(chǎn)生過(guò)多重復(fù)的對(duì)象。啟動(dòng)參數(shù)中內(nèi)存的設(shè)定值過(guò)小。
解決OutOfMemoryError的辦法需要視情況而定,但問(wèn)題的根源在于程序的設(shè)計(jì)不夠合理,需要通過(guò)一些性能檢測(cè)才能找出引發(fā)問(wèn)題的根源。例如,在使用Bitmap對(duì)象時(shí),如果對(duì)象沒(méi)有及時(shí)釋放,長(zhǎng)期運(yùn)行過(guò)程中就很有可能造成OOM意外情況的發(fā)生。因此,及時(shí)回收不再使用的對(duì)象內(nèi)存是避免OOM的重要措施之一。
2、查看GC過(guò)程
1、編寫代碼實(shí)現(xiàn)OOM
import java.util.ArrayList;
public class OOMTest {
public static void main(String[] args) {
ArrayList
long count = 0;
try {
while (true) {
list.add(new OOMTest());
count++;
}
} catch (Error e) {
e.printStackTrace();
}finally{
System.out.println("創(chuàng)建了:" + count);
}
}
}
2、設(shè)置JVM參數(shù)
-Xms1m -Xmx8m -XX:+PrintGCDetails
? ? 運(yùn)行代碼,此時(shí)已經(jīng)發(fā)生OOM錯(cuò)誤,并且在控制臺(tái)打印了相關(guān)的GC信息,但根據(jù)控制臺(tái)打印的信息是很難直接定位到異常的地方,所以我們需要使用分析工具
? ??
3、JProFiler
????????JProfiler?是一個(gè)商業(yè)授權(quán)的Java剖析工具,由EJ技術(shù)有限公司,針對(duì)的Java EE和Java SE應(yīng)用程序開(kāi)發(fā)的。
1、安裝IDEA插件
? ? ? ? file > setting > plugins > jprofiler > install > 重啟IDEA
2、安裝jprofiler
? ? ? ? 下載地址:ej-technologies - Java APM, Java Profiler, Java Installer Builder
3、配置插件
????????file > setting > Tools > JProfiler > 將JProFiler Executable設(shè)置為本機(jī)安裝目錄下\bin\jprofiler.exe
4、使用jprofiler
? ? ? ? 修改JVM參數(shù):
-Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError
? ? ? ? 運(yùn)行代碼,此時(shí)項(xiàng)目的根目錄下會(huì)有個(gè) .hprof 為后綴的文件,找到他雙擊打開(kāi)這個(gè)文件
點(diǎn)擊Biggest Objects,可以很明顯的看出是哪個(gè)對(duì)象占用了很大的內(nèi)存空間
點(diǎn)擊ThreadJump,能看到具體異常的代碼行數(shù)
柚子快報(bào)邀請(qǐng)碼778899分享:JVM(java虛擬機(jī))
精彩鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。