談到Java內(nèi)存模型,指的就是運行時數(shù)據(jù)區(qū),按照線程是否共享可按圖示劃分
1.程序計數(shù)器:可以看作當前線程所執(zhí)行的字節(jié)碼的行號指示器,它是邏輯計數(shù)器,不是物理的,通過改變計數(shù)器的值來選取下一條要執(zhí)行的指令,每個線程有獨立的計數(shù)器,只為java方法計數(shù),不發(fā)生內(nèi)存泄漏
2.虛擬機棧:描述的是java方法執(zhí)行時的線程模型,包含了單個線程每個方法執(zhí)行的棧幀,棧幀存儲了局部變量表,操作數(shù)棧,動態(tài)鏈接,方法出口等信息,每一個方法被調(diào)用直至執(zhí)行完畢的過程,對應著一個棧幀在虛擬機棧中從入棧到出棧的過程
①局部變量表:包含方法執(zhí)行過程中的所有變量
②操作數(shù)棧:負責入棧、出棧等操作
該內(nèi)存規(guī)定了兩種異常:遞歸引發(fā)的StackOverflowError(遞歸過深,棧楨數(shù)超過虛擬棧深度)和虛擬機棧過多引發(fā)的OutOfMemoryError(當虛擬機??梢詣討B(tài)擴展時,如果無法申請足夠多的內(nèi)存,就會拋出異常)
3.本地方法棧:與虛擬機棧發(fā)揮的作用是相似的,區(qū)別只是虛擬機棧為虛擬機執(zhí)行java方法服務(wù),本地方法棧為Native(本地方法)服務(wù)
4.Java堆: Java堆是被所有線程共享的一塊內(nèi)存區(qū)域,在虛擬機啟動時創(chuàng)建。此內(nèi)存區(qū)域的唯一目的就是存放對象實例,邏輯上連續(xù),物理上可不連續(xù),既可以被實現(xiàn)成固定大小的,也可以設(shè)置成可擴展的。當類加載器讀取了類文件后,需要把類、方法、常量、變量放到堆內(nèi)存中,保存所有引用類型的真實信息,以方便執(zhí)行器執(zhí)行。
常量池:存放編譯器生成的字面量和符號引用量
JVM三大性能調(diào)優(yōu)參數(shù)-Xms -Xmx -Xss
在調(diào)用java指令執(zhí)行程序時,通過傳入以上三個參數(shù)分別調(diào)整java堆以及線程所占內(nèi)存的大小
5.元空間:存儲的是運行環(huán)境必須的類信息,取代永久代,不在Java虛擬機的堆中實現(xiàn),而是使用本機物理內(nèi)存實現(xiàn)。
補充:在Java8中,永久代已經(jīng)被移除,被一個稱為元空間的區(qū)域所取代。元空間的本質(zhì)和永久代類似。
元空間與永久代之間最大的區(qū)別在于:永久代使用的JVM的堆內(nèi)存(但是邏輯上是非堆的),但是java8以后的元空間并不在虛擬機中而是使用本機物理內(nèi)存(所以在上圖中,我用虛線表示)。
永久代:是一個常駐內(nèi)存的區(qū)域,用于存放JDK自身所攜帶的Class,Interface的元數(shù)據(jù),即存儲的是運行環(huán)境必須的類信息,被轉(zhuǎn)載進此區(qū)域的數(shù)據(jù)是不會被垃圾回收的,只有關(guān)閉JVM才會釋放此區(qū)域所占用的內(nèi)存空間。
元空間:取代永久代,不在Java虛擬機的堆中實現(xiàn),而是使用本機物理內(nèi)存實現(xiàn)。默認情況下元空間大小僅受本地內(nèi)存限制。類的元數(shù)據(jù)放入native memory,字符串常量在Java堆中(運行時常量和基本類型常量在元空間——方法區(qū))
PS:jdk1.8,jvm把字符串常量池移到了堆內(nèi)存里。此時方法區(qū)=元空間
方法區(qū)(Method Area)并不是所謂的存儲方法的區(qū)域,而是供各線程共享的運行時內(nèi)存區(qū)域。它存儲了已被虛擬機加載的類信息、方法信息、字段信息、常量(被final修飾)、靜態(tài)變量、即時編譯器編譯后的代碼緩存等。
方法區(qū)也是一種規(guī)范,在不同虛擬機里頭實現(xiàn)是不一樣的,最典型的實現(xiàn)就是HotSpot虛擬機Java8之前的永久代(PermGen space)和Java8的元空間(Metaspace)。