當(dāng)你運(yùn)行一個(gè)Java程序的時(shí)候怯疤,需要開辟一定的空間存放代碼執(zhí)行所需要的數(shù)據(jù),
通常稱之為JVM內(nèi)存催束。
雖然如果你只是單純的寫程序集峦,了解JVM內(nèi)存的細(xì)節(jié)沒(méi)有太大的必要,
但隨著你越深入的了解Java的內(nèi)在機(jī)制,比如做一些性能優(yōu)化的時(shí)候塔淤,
你會(huì)越來(lái)越發(fā)現(xiàn)了解JVM內(nèi)存的重要性摘昌。
當(dāng)你對(duì)JVM內(nèi)存有了足夠的了解的時(shí)候,
你在應(yīng)付內(nèi)存管理高蜂,性能優(yōu)化時(shí)就能更加從容自若聪黎,
作為一個(gè)程序員所必要有的好奇心來(lái)說(shuō),
理解JVM是如何分配內(nèi)存备恤,以及垃圾回收器(GC)是如何工作的本身也是一個(gè)愉快的過(guò)程稿饰。
JVM內(nèi)存模型
執(zhí)行一個(gè)程序的過(guò)程中,JVM會(huì)將JVM內(nèi)存分成幾個(gè)部分露泊。
有些內(nèi)存塊會(huì)隨著JVM的啟動(dòng)而被分配并隨著JVM的退出而消亡喉镰,
有些則是線程級(jí)的數(shù)據(jù)塊。
也就是說(shuō)隨著線程的生命周期共存惭笑。
下面是一個(gè)基本的JVM內(nèi)存結(jié)構(gòu)侣姆。
下面簡(jiǎn)單的說(shuō)一下這幾個(gè)部分吧。
堆區(qū)
堆區(qū)隨著JVM的啟動(dòng)而被分配沉噩,主要負(fù)責(zé)存儲(chǔ)生成的class實(shí)例和數(shù)組捺宗。
堆的大小可以是固定的,或者是動(dòng)態(tài)調(diào)整的(基于系統(tǒng)配置)川蒙,
并且分配到堆上的數(shù)據(jù)并不需要是連續(xù)的偿凭。
JVM允許開發(fā)者或者用戶配置堆區(qū)的初始化大小,堆是否允許動(dòng)態(tài)的伸縮派歌,以及堆的最大最小值等等。
如果需要分配內(nèi)存但是堆已經(jīng)占滿的情況下痰哨,會(huì)拋出一個(gè)OurOfMeoryError胶果。
方法區(qū)和運(yùn)行時(shí)常量池
方法區(qū)主要用來(lái)存放每個(gè)class的結(jié)構(gòu),
例如:運(yùn)行時(shí)常量斤斧,字段和方法早抠,方法和構(gòu)造函數(shù)的代碼等。
方法區(qū)也是在JVM啟動(dòng)時(shí)被創(chuàng)建撬讽。
盡管從邏輯上來(lái)說(shuō)它是堆區(qū)的一部分但是不能被GC給回收蕊连,
相反對(duì)于堆的垃圾回收卻是強(qiáng)制的。
方法區(qū)可以是一個(gè)固定的大小游昼,也可以根據(jù)需要擴(kuò)張或者壓縮甘苍。
方法區(qū)上的內(nèi)存分配同樣不必是連續(xù)的。
當(dāng)JVM無(wú)法為方法區(qū)分配更多的內(nèi)存時(shí)烘豌,會(huì)拋出OutOfMemoryError
JVM棧
對(duì)于每一個(gè)線程都有一個(gè)私有的棧载庭。
堆存儲(chǔ)幀。
棧幀用來(lái)存儲(chǔ)數(shù)據(jù),臨時(shí)結(jié)果囚聚,動(dòng)態(tài)鏈接靖榕,返回函數(shù)值,拋出異常等顽铸。
棧維護(hù)了局部變量茁计,臨時(shí)結(jié)果并且在函數(shù)執(zhí)行和返回上起到作用。
除了將棧幀壓入和壓出之外谓松,我們不會(huì)直接操作棧本身星压。
棧幀也是在堆上被分配的(HotSpot VM)。
同樣棧上的內(nèi)存分配也不需要是連續(xù)的毒返。
正是因?yàn)檫@樣的規(guī)定允許了椬饽唬可以是固定或者動(dòng)態(tài)調(diào)整的。
如果我們使用固定大小的棧拧簸,那么對(duì)于每一個(gè)棧在初始化時(shí)所分配的大小都是不一樣的劲绪。
如果棧上的內(nèi)存不夠分配的時(shí)候,JVM會(huì)拋出一個(gè)StackOverflowError
如果JVM椗璩啵可以動(dòng)態(tài)擴(kuò)展贾富,但是當(dāng)棧在初始化或者在動(dòng)態(tài)擴(kuò)展時(shí)所需的內(nèi)存不夠時(shí),
JVM會(huì)拋出一個(gè)OutOfMemoryError
本地方法棧
本地方法棧又叫做C棧牺六。
它支持了本地方法(通常由其他語(yǔ)言寫成)在執(zhí)行時(shí)每個(gè)線程上的內(nèi)存分配颤枪。
基本上邏輯和JVM棧相似。
PC寄存器(程序計(jì)數(shù)器)
每一個(gè)JVM線程都有其獨(dú)立的程序計(jì)數(shù)器淑际。
任何時(shí)候畏纲,每個(gè)JVM線程都在執(zhí)行某個(gè)單獨(dú)的方法,通常叫做當(dāng)前方法春缕。
因?yàn)镴ava程序也包含本地代碼(例如本地庫(kù))盗胀,因此我們需要兩種不同的方式來(lái)處理程序計(jì)數(shù)器。
當(dāng)方法不是本地方法時(shí)锄贼,程序計(jì)數(shù)器將包含當(dāng)前執(zhí)行的JVM指令的地址票灰,
否則標(biāo)記為undefined。
這些就是關(guān)于JVM內(nèi)存模型的一個(gè)簡(jiǎn)單介紹宅荤。
A 參考
https://howtodoinjava.com/java/garbage-collection/jvm-memory-model-structure-and-components/
https://docs.oracle.com/cd/B28359_01/server.111/b31107/asmcon.htm#OSTMG036