教你JVM怎么使用native memory
JRE如何使用native存儲(chǔ)
今天看到一篇特別好的文章,翻譯其中一小段Understanding how the JVM uses native memory
Runtime環(huán)境提供了被某些未知的用戶代碼驅(qū)動(dòng)的能力,這使runtime在任何情況下都能使用合適的資源。每一個(gè)JVM管理的java應(yīng)用的行為都會(huì)潛在的影響JVM所能提供的運(yùn)行時(shí)環(huán)境。這一節(jié)我們討論為什么Java應(yīng)用會(huì)消耗native存儲(chǔ)
Java堆和GC
Java的堆是用來(lái)存儲(chǔ)分配對(duì)象的一塊內(nèi)存,大多數(shù)的JVM有一塊邏輯堆內(nèi)存,也有少數(shù)的JVM實(shí)現(xiàn)了多塊堆存儲(chǔ)。一個(gè)物理內(nèi)存可以基于GC被分配成多塊邏輯上的內(nèi)存。
The Just-in-time (JIT) compiler
JIT編譯器會(huì)把java字節(jié)碼編譯成運(yùn)行時(shí)可以直接運(yùn)行的機(jī)器碼,這極大的提升了JRE運(yùn)行速度,使Java代碼運(yùn)行比肩native code。
字節(jié)碼編譯會(huì)使用native內(nèi)存(同理,一些像GCC這樣的編譯器也需要內(nèi)存去run),但是JIT的輸入(字節(jié)碼)輸出(機(jī)器碼)都必須存儲(chǔ)在native內(nèi)存中。所以包含很多JIT-compiled的方法的應(yīng)用相對(duì)來(lái)說(shuō)更占用native內(nèi)存。
Classes and classloaders
Java程序由定義了對(duì)象和方法邏輯的類組成,可能是Java運(yùn)行時(shí)的庫(kù)(比如java.lang.String),也可能是三方庫(kù)。這些class在被使用的時(shí)候會(huì)被加載進(jìn)來(lái)并被存儲(chǔ)在內(nèi)存里面。
class如何被存儲(chǔ)不同JVM的實(shí)現(xiàn)相差極大。Sun JDK存儲(chǔ)在永生帶(PermGen),IBM從Java5開(kāi)始為每個(gè)classloader開(kāi)辟native內(nèi)存并將它們存儲(chǔ)在那里。具體的存儲(chǔ)位置需要查看實(shí)現(xiàn)的文檔。
顯而易見(jiàn)的是,用更多的類會(huì)消耗更多的內(nèi)存。(這意味著你的native內(nèi)存消耗會(huì)持續(xù)增加,或者明確的開(kāi)辟一塊內(nèi)存,像PermGen,去容納所有的class),需要注意的是不止是你的應(yīng)用的class需要存儲(chǔ),frameworks,application servers,三方庫(kù),JRE這里面的class在被用到的時(shí)候都會(huì)被加載并存儲(chǔ)進(jìn)來(lái)。
JRE允許卸載class去回收空間,但是這僅僅是在內(nèi)存嚴(yán)重不足的情況下。不可能僅僅卸載一個(gè)單獨(dú)的class文件,而是卸載classloader,和它加載進(jìn)來(lái)的所有class,一個(gè)classloader僅僅會(huì)在以下情況下被卸載:
- Java堆中不包含任何代表此classloader的java.lang.ClassLoader對(duì)象的應(yīng)引用
- Java堆中不包含任何代表由此classloader加載進(jìn)來(lái)的類的java.lang.Class對(duì)象的引用
- Java堆中沒(méi)有任何被此classloader加載進(jìn)來(lái)的對(duì)象存活。
JNI
JNI允許本地代碼和java代碼相互調(diào)用。JRE嚴(yán)重依賴JNI代碼去實(shí)現(xiàn)文件和網(wǎng)絡(luò)這些類庫(kù)的功能,一個(gè)JNI應(yīng)用能以三種方式增加JRE的native內(nèi)存
- JNI應(yīng)用的native代碼會(huì)被編譯進(jìn)一個(gè)so動(dòng)態(tài)鏈接庫(kù),運(yùn)行時(shí)會(huì)被加載到可執(zhí)行的地址空間呢,大型native應(yīng)用程序只需加載就可占據(jù)進(jìn)程地址空間的很大一部分。
- native代碼必須跟JVM共享內(nèi)存,任何native代碼分配或者映射所需要的native內(nèi)存都需要占用JVM的內(nèi)存。
- 某些JNI方法可以使用native作為他們正常操作的一部分,比如GetTypeArrayElements或者GetTypeArrayRegion方法都可以拷貝Java堆內(nèi)存到到native內(nèi)存供native代碼使用。以這種方式訪問(wèn)大塊的Java堆內(nèi)存相應(yīng)的會(huì)占用大量的native內(nèi)存
NIO
NIO是java1.4之后添加的API,基于管道和緩存,以一種新的方式實(shí)現(xiàn)IO操作。除了基于堆的I/O,NIO還添加了基于native內(nèi)存的direct ByteBuffer(通過(guò)java.nio.ByteBuffer.allocateDirect()方法分配)。Direct ByteBuffers可以直接調(diào)用系統(tǒng)庫(kù)的方法去實(shí)現(xiàn)I/O操作,這會(huì)顯示提升在某些場(chǎng)景下的執(zhí)行效率,因?yàn)槟鼙苊庠贘ava堆和native堆之間拷貝數(shù)據(jù)。
我們可能會(huì)疑惑direct ByteBuffer申請(qǐng)的內(nèi)存到底存在哪里,應(yīng)用仍然用的是Java堆里面的對(duì)象去完成I/O操作,但是持有數(shù)據(jù)的緩存仍然存在native內(nèi)存中 -Java堆的對(duì)象只是持有了一個(gè)native堆緩存的引用。一個(gè)non-direct ByteBuffer則是直接在Java堆中存儲(chǔ)了byte[]數(shù)組。

Memory topology for direct and non-direct java.nio.ByteBuffers
Java堆發(fā)生GC的時(shí)候同樣會(huì)對(duì)Direct ByteBuffer數(shù)據(jù)執(zhí)行清除native緩存操作,GC僅僅會(huì)在Java堆中已經(jīng)滿了,不支持新的堆空間分配或者程序手動(dòng)調(diào)用GC(不建議手動(dòng)調(diào)用GC)的情況下發(fā)生。
還有一種情況,native內(nèi)存已經(jīng)滿了,又有代碼來(lái)請(qǐng)求native內(nèi)存,但是這個(gè)時(shí)候Java堆還沒(méi)有達(dá)到GC的條件,所以并不會(huì)發(fā)生GC。(也就是說(shuō)native內(nèi)存的GC完全依賴Java堆的GC,反之如果native需要GC了但是堆沒(méi)有GC的需求的則不會(huì)引發(fā)GC)
Threads
應(yīng)用的每一個(gè)線程都需要內(nèi)存去儲(chǔ)存它的棧(這塊內(nèi)存用來(lái)存儲(chǔ)本地變量表和保存狀態(tài)),每一個(gè)Java線程都需要棧去執(zhí)行,根據(jù)實(shí)現(xiàn),Java線程可以具有單獨(dú)的native和Java棧。除了堆??臻g之外,每個(gè)線程還需要一些native內(nèi)存用于thread-local存儲(chǔ)和內(nèi)部數(shù)據(jù)結(jié)構(gòu)。
堆棧大小因Java實(shí)現(xiàn)和架構(gòu)而異。某些實(shí)現(xiàn)允許您指定Java線程的堆棧大小。通常在256KB和756KB之間的值。
盡管每個(gè)線程使用的內(nèi)存量非常小,但對(duì)于具有數(shù)百個(gè)線程的應(yīng)用程序,線程堆棧的總內(nèi)存使用量可能很大。運(yùn)行具有比可用處理器多的線程來(lái)運(yùn)行它們的應(yīng)用程序通常是低效的,并且可能導(dǎo)致性能低下以及增加的內(nèi)存使用。
以上就是教你JVM怎么使用native memory的詳細(xì)內(nèi)容,更多關(guān)于JVM使用native memory的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Visual Studio 2022 集成 GIT 使用圖文教程
本文詳細(xì)介紹了VisualStudio2022與GIT集成的操作步驟,包括下載GIT、安裝和配置GIT、以及如何在VisualStudio2022中下載GIT項(xiàng)目和上傳項(xiàng)目到GIT服務(wù)器,感興趣的朋友跟隨小編一起看看吧2024-09-09
如何通過(guò)memberlist庫(kù)實(shí)現(xiàn)gossip管理集群及集群數(shù)據(jù)交互問(wèn)題
這篇文章主要介紹了通過(guò)memberlist庫(kù)實(shí)現(xiàn)gossip管理集群以及集群數(shù)據(jù)交互,本文介紹了memberlist庫(kù)的簡(jiǎn)單用法,結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-07-07
ffmpeg網(wǎng)頁(yè)視頻流m3u8 ts實(shí)現(xiàn)視頻下載
這篇文章主要為大家介紹了ffmpeg網(wǎng)頁(yè)視頻流m3u8 ts實(shí)現(xiàn)視頻下載詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
git如何從某個(gè)分支的指定歷史版本中創(chuàng)建新分支
這篇文章主要介紹了git如何從某個(gè)分支的指定歷史版本中創(chuàng)建新分支問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05
解析scratch3.0二次開(kāi)發(fā)之scratch-blocks免編譯修改問(wèn)題
大家在使用scratch-blocks編譯時(shí)會(huì)遇到scratch-gui依賴的scratch-blocks模塊在安裝的時(shí)候編譯會(huì)報(bào)錯(cuò),針對(duì)這個(gè)問(wèn)題我們?cè)撛趺唇鉀Q呢,下面小編給大家?guī)?lái)了scratch3.0二次開(kāi)發(fā)之scratch-blocks免編譯修改方法,感興趣的朋友一起看看吧2021-08-08

