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