教你JVM怎么使用native memory
JRE如何使用native存儲
今天看到一篇特別好的文章,翻譯其中一小段Understanding how the JVM uses native memory
Runtime環(huán)境提供了被某些未知的用戶代碼驅(qū)動的能力,這使runtime在任何情況下都能使用合適的資源。每一個JVM管理的java應(yīng)用的行為都會潛在的影響JVM所能提供的運(yùn)行時環(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é)碼編譯成運(yùn)行時可以直接運(yùn)行的機(jī)器碼,這極大的提升了JRE運(yùn)行速度,使Java代碼運(yùn)行比肩native code。
字節(jié)碼編譯會使用native內(nèi)存(同理,一些像GCC這樣的編譯器也需要內(nèi)存去run),但是JIT的輸入(字節(jié)碼)輸出(機(jī)器碼)都必須存儲在native內(nèi)存中。所以包含很多JIT-compiled的方法的應(yīng)用相對來說更占用native內(nèi)存。
Classes and classloaders
Java程序由定義了對象和方法邏輯的類組成,可能是Java運(yùn)行時的庫(比如java.lang.String),也可能是三方庫。這些class在被使用的時候會被加載進(jìn)來并被存儲在內(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在被用到的時候都會被加載并存儲進(jìn)來。
JRE允許卸載class去回收空間,但是這僅僅是在內(nèi)存嚴(yán)重不足的情況下。不可能僅僅卸載一個單獨的class文件,而是卸載classloader,和它加載進(jìn)來的所有class,一個classloader僅僅會在以下情況下被卸載:
- Java堆中不包含任何代表此classloader的java.lang.ClassLoader對象的應(yīng)引用
- Java堆中不包含任何代表由此classloader加載進(jìn)來的類的java.lang.Class對象的引用
- Java堆中沒有任何被此classloader加載進(jìn)來的對象存活。
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代碼會被編譯進(jìn)一個so動態(tài)鏈接庫,運(yùn)行時會被加載到可執(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代碼使用。以這種方式訪問大塊的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ùn)行具有比可用處理器多的線程來運(yùn)行它們的應(yīng)用程序通常是低效的,并且可能導(dǎo)致性能低下以及增加的內(nèi)存使用。
以上就是教你JVM怎么使用native memory的詳細(xì)內(nèi)容,更多關(guān)于JVM使用native memory的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
VScode?隱藏大量無用的文件比如在看Linux?kernel或boot時候
這篇文章主要介紹了VScode?隱藏大量無用的文件比如在看Linux?kernel或boot時候,VScode 工程創(chuàng)建先在 Ubuntu 下編譯一下 uboot,然后將編譯后的 uboot 文件夾復(fù)制到 windows 下,并創(chuàng)建VScode 工程,需要的朋友可以參考下2022-10-10將Git存儲庫克隆到本地IntelliJ IDEA項目中的詳細(xì)教程
這篇文章主要介紹了將Git存儲庫克隆到本地IntelliJ IDEA項目中的詳細(xì)教程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-10-10手把手教你將Vim改裝成一個IDE編程環(huán)境(圖文) 吳垠
這篇文章主要介紹了手把手教你將Vim改裝成一個IDE編程環(huán)境(圖文) 吳垠 ,需要的朋友可以參考下2016-01-01分享VSCOCE遠(yuǎn)程連接服務(wù)器的一次錯誤記錄(推薦)
這篇文章主要介紹了VSCOCE遠(yuǎn)程連接服務(wù)器的一次錯誤記錄,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-04-04