您好,登錄后才能下訂單哦!
這篇文章主要講解了“Java虛擬機(jī)內(nèi)存管理知識(shí)有哪些”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“Java虛擬機(jī)內(nèi)存管理知識(shí)有哪些”吧!
Java虛擬機(jī)規(guī)范將物理內(nèi)存(主內(nèi)存和CPU中的緩存、寄存器)劃分為 程序計(jì)數(shù)器 、 Java 虛擬機(jī)棧 、 本地方法棧 、 Java 堆 、 方法區(qū) 五個(gè)區(qū)域,但并沒(méi)有規(guī)定這些區(qū)域的具體實(shí)現(xiàn),在其他地方聽(tīng)到的一些名詞(如永久代、元空間等,這些都是方法區(qū)的具體實(shí)現(xiàn))可能都是這些區(qū)域具體的實(shí)現(xiàn),這點(diǎn)要特別注意,別被這些概念搞暈。
各個(gè)區(qū)域的特點(diǎn)如下表:
區(qū)域 | 線程關(guān)系 | 內(nèi)存異常 | 垃圾回收 | 作用 |
---|---|---|---|---|
程序計(jì)數(shù)器 | 線程私有 | 無(wú) | 無(wú) | 記錄Java虛擬機(jī)正在指向的字節(jié)碼指令 |
Java 虛擬機(jī)棧 | 線程私有 | StackOverflowError、OutOfMemoryError | 無(wú) | 描述 Java 方法執(zhí)行時(shí)的內(nèi)存模型,棧中棧幀存儲(chǔ)局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、方法返回地址等信息。 |
本地方法棧 | 線程私有 | StackOverflowError、OutOfMemoryError | 無(wú) | 描述本地方法(非 Java 代碼編寫)執(zhí)行時(shí)的內(nèi)存模型 |
方法區(qū) | 線程共享 | OutOfMemoryError | 有 | 存儲(chǔ)虛擬機(jī)加載過(guò)的類信息、常量(常量池)、靜態(tài)變量、即時(shí)編譯器(JIT)生成的代碼 |
Java 堆 | 線程共享 | OutOfMemoryError | 有 | 存放 Java對(duì)象(實(shí)例) |
類加載器分為 Bootstrap 、 Extension ClassLoader (Java9 中是 Platform ClassLoader)、 Application ClassLoader ,級(jí)別也是從低到高。
可以調(diào)用類加載器對(duì)象的 getParent() 方法查找該級(jí)加載器的上一級(jí)加載器,也成為父類加載器。
類加載器 | 描述 | 是否為 Java 實(shí)現(xiàn) |
---|---|---|
Bootstrap | JVM啟動(dòng)時(shí)創(chuàng)建,通常由操作系統(tǒng)相關(guān)的本地代碼實(shí)現(xiàn),是最根基的類加載器,負(fù)責(zé)裝載的是最核心的 Java 類,如 Object 類、System 類、String 類等 | 否 |
Extension ClassLoader | 加載一些擴(kuò)展的系統(tǒng)類,如 XML、加密、壓縮相關(guān)功能的類 | 是 |
Application ClassLoader | 加載用戶定義的 CLASSPATH 路徑下的類 | 是 |
此處不翻譯了,翻譯后就變味了,尤其是下面的 Parents Delegation Model 翻譯為雙親委派模型很不恰當(dāng)。
字節(jié)碼文件加載到內(nèi)存中,才可以實(shí)例化出類,而類加載器就是負(fù)責(zé)加載 Java 類的。低級(jí)別的類加載器在加載一個(gè)類時(shí)會(huì)先詢問(wèn)上一級(jí)的類加載器,直到詢問(wèn)到頂級(jí)的類加載器(Bootstrap),如果頂級(jí)的類加載器可以加載就加載該類,否則向下嘗試是否可以加載該類,也即是如果上一級(jí)類加載器能加載的就用上一級(jí)加載(復(fù)用上一級(jí)的類加載器),用不了再用自身的類加載器加載,這也就是口口相傳卻是翻譯很不恰當(dāng)?shù)碾p親委派模型。這樣做可以使類加載更加安全,避免加載和標(biāo)準(zhǔn) Java 類同包同名的類破壞虛擬機(jī)。
可以根據(jù)需要繼承 Application ClassLoader 實(shí)現(xiàn)自定義類加載器,隔離加載器、修改類的加載方式、擴(kuò)展加載源、防止源碼泄露。
類加載是將字節(jié)碼文件實(shí)例化成 Class 對(duì)象并進(jìn)行相關(guān)初始化的過(guò)程。類加載包括類的 加載(Load)、類的 鏈接 (Link)、類的 初始化 (init)三個(gè)步驟。
類的加載是將字節(jié)碼文件以二進(jìn)制流的方式讀取到內(nèi)存中并轉(zhuǎn)化為特定的數(shù)據(jù)結(jié)構(gòu),檢查 cafe baby 這個(gè)魔法數(shù)(是不是Java文件的標(biāo)志),是否有父類等,創(chuàng)建類對(duì)應(yīng)的 Class 對(duì)象。
類的鏈接又分為 驗(yàn)證 、 準(zhǔn)備 、 解析 三個(gè)階段,驗(yàn)證階段是進(jìn)行更加詳細(xì)的校驗(yàn),如類型是否正確,靜態(tài)變量是否合理等;準(zhǔn)備階段是為類的靜態(tài)變量分配內(nèi)存空間,并設(shè)定默認(rèn)值;解析階段是保證類和類之間相互引用的正確性,完成類在內(nèi)存中的結(jié)構(gòu)布局。
類的初始化并不是初始化對(duì)象,而是根據(jù)代碼中的值初始化類的靜態(tài)變量值,類的靜態(tài)變量的初始化方式也有直接在聲明時(shí)指定值和在靜態(tài)代碼塊中指定值兩種方式。
Java虛擬機(jī)棧中的局部變量表存放的數(shù)據(jù)除了基本的數(shù)據(jù)類型外,還有對(duì)象的引用類型(reference),這關(guān)系到如何訪問(wèn)一個(gè)對(duì)象。
在不同的虛擬機(jī)中,對(duì)象的訪問(wèn)方式也是不同的,主流的訪問(wèn)方式有 使用句柄 和 直接指針 兩種。
使用句柄:
使用句柄是在 Java 堆中劃分出一塊區(qū)域作為句柄池,句柄池中存放對(duì)象的實(shí)例數(shù)據(jù)和類型數(shù)據(jù)(類相關(guān)的信息),reference 中存放的是對(duì)象在句柄中的地址,這是一種間接訪問(wèn)對(duì)象方式。
直接指針:
直接指針是reference中直接存放對(duì)象的地址,但 Java 堆需要考慮如何存放訪問(wèn)對(duì)象類型的指針。
兩種方式其實(shí)各有優(yōu)劣,如下表:
方式 | 優(yōu)勢(shì) | 特點(diǎn) |
---|---|---|
使用句柄 | reference 中存放的是穩(wěn)定的句柄地址,對(duì)象在移動(dòng)時(shí)只改變句柄池中對(duì)象的地址,而reference中的地址不需要改變。 | 間接訪問(wèn) |
直接指針 | 節(jié)省了一次指針定位的時(shí)間開(kāi)銷,訪問(wèn)速度相對(duì)更快。 | 直接訪問(wèn) |
垃圾回收之前需要判斷對(duì)象是否可以回收,常見(jiàn)的判斷算法有引用計(jì)數(shù)算法和可達(dá)性分析算法。
每個(gè)對(duì)象都有對(duì)應(yīng)的引用計(jì)數(shù)器,當(dāng)有一個(gè)地方引用該對(duì)象時(shí),就將引用計(jì)數(shù)器的值加1,當(dāng)引用失效時(shí),就將引用計(jì)數(shù)器的值減1,當(dāng)計(jì)數(shù)器的值為0時(shí),表示對(duì)象沒(méi)有引用,可以被回收了。
缺點(diǎn):看起來(lái)簡(jiǎn)單高效,但是有循環(huán)引用問(wèn)題。如果兩個(gè)對(duì)象中包含對(duì)方的引用就會(huì)產(chǎn)生循環(huán)引用問(wèn)題,導(dǎo)致垃圾收集器不能回收對(duì)象。
如果對(duì)象與GC Roots 之間沒(méi)有直接或間接的應(yīng)用關(guān)系,就可以被回收了。常見(jiàn)的 GC Roots 對(duì)象包括虛擬機(jī)棧(棧幀本地變量表)中引用的對(duì)象、方法區(qū)中靜態(tài)屬性引用的對(duì)象、方法區(qū)常量引用的對(duì)象、本地方法棧中(Native 方法)引用的對(duì)象。GC Roots,是一個(gè)特殊的對(duì)象,且絕對(duì)不能被其他對(duì)象引用,不然也會(huì)像引用計(jì)數(shù)算法那樣有循環(huán)引用的問(wèn)題。
注:歡迎工作1到6年的Java工程師朋友們加入Java架構(gòu)交流裙:834962734。群內(nèi)提供免費(fèi)的Java架構(gòu)學(xué)習(xí)資料(有Spring,MyBatis,Netty源碼分析,高并發(fā)、高性能、分布式、微服務(wù)架構(gòu)的原理,JVM性能優(yōu)化等...)這些成為架構(gòu)師必備的知識(shí)體系,以及Java進(jìn)階學(xué)習(xí)路線圖。
標(biāo)記-清除算法
最基本的垃圾回收算法,后續(xù)的算法都是對(duì)它的改進(jìn)。
首先標(biāo)記出需要回收的對(duì)象,再將標(biāo)記出的區(qū)域內(nèi)容清除。
缺點(diǎn)是:標(biāo)記時(shí)的查找效率,清除時(shí)產(chǎn)生內(nèi)存碎片。
標(biāo)記-復(fù)制算法
將內(nèi)存區(qū)域劃分為兩塊,每次只使用一塊,垃圾回收時(shí),標(biāo)記正在使用的內(nèi)存區(qū)域,將存活的對(duì)象復(fù)制到另一塊內(nèi)存區(qū)域,再將原來(lái)的那一塊內(nèi)存區(qū)域一次性清除。避免了內(nèi)存碎片的產(chǎn)生,但不適合存活時(shí)間長(zhǎng)的對(duì)象。
缺點(diǎn):浪費(fèi)了一半的內(nèi)存空間,當(dāng)對(duì)象存活率高時(shí),進(jìn)行大量的復(fù)制操作,效率不高。
標(biāo)記-整理算法
標(biāo)記過(guò)程和標(biāo)記-除算法相同,垃圾回收時(shí),是將存活的對(duì)象向同一端移動(dòng),再清除這之外的內(nèi)存區(qū)域,這樣就使得對(duì)象占用的內(nèi)存區(qū)域連續(xù),避免了內(nèi)存碎片的產(chǎn)生。
分代收集算法
根據(jù)對(duì)象存活時(shí)間的長(zhǎng)短,將堆內(nèi)存分為新生代和老生代,存活時(shí)間短的對(duì)象放在新生代區(qū)域,存活時(shí)間長(zhǎng)的大對(duì)象(如對(duì)象數(shù)組)放在老生代區(qū)域。新生代和老生代的比例是 1 : 2,新生代又分為一個(gè) Eden 區(qū)和兩個(gè) Survivor 區(qū)。新生代使用標(biāo)記-復(fù)制算法,老生代使用標(biāo)記-清除算法或標(biāo)記-整理算法,這樣最大發(fā)揮各自算法的優(yōu)勢(shì)。
Serial 回收器
Serial 采取 “復(fù)制算法” 實(shí)現(xiàn),如果是在單 CPU 環(huán)境下,Serial 收集器沒(méi)有線程交互的開(kāi)銷,理論上是可以獲得最高的單線程執(zhí)行效率,STW 的時(shí)間也可以控制在幾十到幾百毫秒內(nèi),這個(gè)時(shí)間是完全可以接受的。
Serial Old (PS MarkSweep)回收器
Serial Old 收集器 是 Serial 收集器的老年代版本,同樣也是一個(gè)單線程收集器,使用了 “標(biāo)記-整理算法”。
ParNew 回收器
ParNew 收集器實(shí)際上就是 Serial 收集器的多線程版本,收集算法、STW、對(duì)象分配的規(guī)則、回收策略等都與 Serial 收集器完全一樣,兩者相同的代碼很多。ParNew 收集器雖然有多線程優(yōu)勢(shì),但在單 CPU 和多 CPU 環(huán)境下,效果并不一定會(huì)比 Serial 好,至少在單 CPU 環(huán)境下是肯定不如的 Serial 的。
Parallel Scavenge 回收器
Parallel Scavenge收集器和 ParNew 收集器很像,也是一個(gè)新生代收集器,也是使用復(fù)制算法,并且還是并行的多線程的收集器。相比于 ParNew 收集器,Parallel Scavenge收集器可以更加精準(zhǔn)的控制 CPU 的吞吐量和 STW 的時(shí)間,對(duì)于交互不多的任務(wù)可以更快地完成。
Parallel Old 回收器
Parallel Old 收集器是 Parallel Scavenge 收集器的老年代版本,使用多線程和 “標(biāo)記-整理算法”。在 Parallel Old 收集器出現(xiàn)之間,選擇了 Parallel Scavenge 收集器作為新生代的收集器,就只能選擇 Serial Old 收集器作為老生代收集器,這樣肯定就是對(duì)多 CPU 的浪費(fèi),所以 Parallel Scavenge收集器 + Parallel Old 收集器,對(duì)于多 CPU 環(huán)境吞吐量要求高的環(huán)境,算是強(qiáng)強(qiáng)聯(lián)合。
CMS 回收器
CMS (Concurrent Mark Sweep)收集器從英文名字上看就是基于 “標(biāo)記-清除算法” 實(shí)現(xiàn)的,并且還有并發(fā)的特點(diǎn),它是一種以縮短 STW 的時(shí)間為目標(biāo)的收集器,對(duì)于一些重視服務(wù)響應(yīng)速度的網(wǎng)站,肯定是 STW 越短,用戶體驗(yàn)越好,但是缺點(diǎn)是會(huì)在垃圾收集結(jié)束后產(chǎn)生大量的空間碎片。
通過(guò)初始標(biāo)記(Initial Mark)、并發(fā)標(biāo)記(Concurrent Mark)、重新標(biāo)記(Remark)、并發(fā)清除(Concurrent Sweep)四個(gè)步驟完成垃圾回收。
G1 回收器
G1 收集器是目前最先進(jìn)的收集器,也是 JDK7 之后默認(rèn)的垃圾回收器,它是基于 “標(biāo)記-復(fù)制算法” 實(shí)現(xiàn)的,所以不會(huì)產(chǎn)生內(nèi)存碎片,并且也可以精準(zhǔn)地控制 STW 的時(shí)間。G1 收集器對(duì)于新生代和老年代都是適用的,優(yōu)先回收垃圾最多的區(qū)域。
感謝各位的閱讀,以上就是“Java虛擬機(jī)內(nèi)存管理知識(shí)有哪些”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)Java虛擬機(jī)內(nèi)存管理知識(shí)有哪些這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。