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

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

