追蹤JVM中的本地內(nèi)存

1.概述

有沒有想過為什么Java應(yīng)用程序通過眾所周知的-Xms和-Xmx調(diào)優(yōu)標(biāo)志消耗的內(nèi)存比指定數(shù)量多得多?出于各種原因和可能的優(yōu)化,JVM可以分配額外的本機(jī)內(nèi)存。這些額外的分配最終會使消耗的內(nèi)存超出-Xmx限制。

創(chuàng)新互聯(lián)公司專注為客戶提供全方位的互聯(lián)網(wǎng)綜合服務(wù),包含不限于成都網(wǎng)站制作、成都網(wǎng)站設(shè)計、察哈爾右翼前網(wǎng)絡(luò)推廣、微信小程序開發(fā)、察哈爾右翼前網(wǎng)絡(luò)營銷、察哈爾右翼前企業(yè)策劃、察哈爾右翼前品牌公關(guān)、搜索引擎seo、人物專訪、企業(yè)宣傳片、企業(yè)代運(yùn)營等,從售前售中售后,我們都將竭誠為您服務(wù),您的肯定,是我們最大的嘉獎;創(chuàng)新互聯(lián)公司為所有大學(xué)生創(chuàng)業(yè)者提供察哈爾右翼前建站搭建服務(wù),24小時服務(wù)熱線:028-86922220,官方網(wǎng)址:bm7419.com

在本教程中,我們將列舉JVM中的一些常見內(nèi)存分配源,以及它們的大小調(diào)整標(biāo)志,然后學(xué)習(xí)如何使用本機(jī)內(nèi)存跟蹤監(jiān)視它們。

2.原生分配

堆通常是Java應(yīng)用程序中最大的內(nèi)存使用者,但還有其他人。除了堆之外,JVM還從本機(jī)內(nèi)存中分配出一個相當(dāng)大的塊來維護(hù)類的元數(shù)據(jù),應(yīng)用程序代碼,JIT生成的代碼,內(nèi)部數(shù)據(jù)結(jié)構(gòu)等。在下面的部分中,我們將探討其中的一些分配。

2.1. Metaspace(元空間)

為了維護(hù)有關(guān)已加載類的一些元數(shù)據(jù),JVM使用名為Metaspace的專用非堆區(qū)域。在Java 8之前,被稱為PermGen或Permanent Generation。 Metaspace或PermGen包含有關(guān)已加載類的元數(shù)據(jù),而不是它們的實例,它們保存在堆中。

這里重要的是堆大小配置不會影響元空間大小,因為Metaspace是一個堆外數(shù)據(jù)區(qū)。為了限制Metaspace大小,我們使用其他調(diào)優(yōu)標(biāo)志:

  • -XX:MetaspaceSize和-XX:MaxMetaspaceSize設(shè)置最小和最大元空間大小
  • 在Java 8之前,-XX:PermSize和-XX:MaxPermSize設(shè)置最小和最大PermGen大小

    2.2. Threads(線程)

    JVM中最耗費(fèi)內(nèi)存的數(shù)據(jù)區(qū)之一是堆棧,與每個線程同時創(chuàng)建。堆棧存儲局部變量和部分結(jié)果,在方法調(diào)用中起著重要作用。

    默認(rèn)的線程堆棧大小取決于平臺,但在大多數(shù)現(xiàn)代64位操作系統(tǒng)中,它大約為1 MB。此大小可通過-Xss調(diào)整標(biāo)志進(jìn)行配置。

    與其他數(shù)據(jù)區(qū)域相比,當(dāng)對線程數(shù)沒有限制時,分配給堆棧的總內(nèi)存實際上是無限制的。值得一提的是,JVM本身需要一些線程來執(zhí)行其內(nèi)部操作,如GC或即時編譯。

    2.3. Code Cache(代碼緩存)

    為了在不同平臺上運(yùn)行JVM字節(jié)碼,需要將其轉(zhuǎn)換為機(jī)器指令。執(zhí)行程序時,JIT編譯器負(fù)責(zé)此編譯。

    當(dāng)JVM將字節(jié)碼編譯為匯編指令時,它會將這些指令存儲在稱為代碼緩存的特殊非堆數(shù)據(jù)區(qū)中??梢韵窆芾鞪VM中的其他數(shù)據(jù)區(qū)一樣管理代碼緩存。 -XX:InitialCodeCacheSize-XX:ReservedCodeCacheSize調(diào)整標(biāo)志確定代碼緩存的初始值和可能最大值。

    2.4. Garbage Collection(垃圾回收)

    JVM附帶了一些GC算法,每個算法適用于不同的用例。所有這些GC算法都有一個共同的特點(diǎn):他們需要使用一些堆外數(shù)據(jù)結(jié)構(gòu)來執(zhí)行他們的任務(wù)。這些內(nèi)部數(shù)據(jù)結(jié)構(gòu)消耗更多本機(jī)內(nèi)存。

    2.5. Symbols(符號)

    讓我們從 Strings 開始,這是應(yīng)用程序和庫代碼中最常用的數(shù)據(jù)類型之一。由于它們無處不在,它們通常占據(jù)堆的很大一部分。如果大量的這些字符串包含相同的內(nèi)容,那么堆的很大一部分將被浪費(fèi)。

    為了節(jié)省一些堆空間,我們可以存儲每個 String 的一個版本,并讓其他版本引用存儲的版本。此過程稱為 String Interning 。由于JVM只能內(nèi)部編譯時間字符串常量,我們可以手動調(diào)用字符串的intern方法來獲取內(nèi)部編譯字符串。

    JVM將實際存儲的字符串存儲在本機(jī)特殊固定大小并稱為字符串表的哈希表中,也稱為字符串池。我們可以通過-XX:StringTableSize調(diào)整標(biāo)志配置表大?。赐暗臄?shù)量)。

    除了字符串表之外,還有另一個稱為運(yùn)行時常量池的本機(jī)數(shù)據(jù)區(qū)域。 JVM使用此池來存儲常量,如編譯時數(shù)字文字或必須在運(yùn)行時解析的方法和字段引用。

    2.6. Native Byte Buffers(本地字節(jié)緩沖區(qū))

    JVM通常有大量分配本機(jī)內(nèi)存的嫌疑,但有時開發(fā)人員也可以直接分配本機(jī)內(nèi)存。最常見的方法是被JNI調(diào)用的malloc和NIO中可直接調(diào)用的ByteBuffers。

    2.7. Additional Tuning Flags(額外的調(diào)整標(biāo)志)

    在本節(jié)中,我們針對不同的優(yōu)化方案使用了少量JVM調(diào)優(yōu)標(biāo)志。使用以下提示,我們幾乎可以找到與特定概念相關(guān)的所有調(diào)優(yōu)標(biāo)志:

    $ java -XX:+PrintFlagsFinal -version | grep <concept>

    PrintFlagsFinal打印JVM中的所有-XX選項。例如,要查找所有與Metaspace相關(guān)的標(biāo)志:

    $ java -XX:+PrintFlagsFinal -version | grep Metaspace
        // truncated
        uintx MaxMetaspaceSize                          = 18446744073709547520                    {product}
        uintx MetaspaceSize                             = 21807104                                {pd product}
        // truncated

    3. 本機(jī)內(nèi)存跟蹤 (NMT)

    現(xiàn)在我們已經(jīng)了解了JVM中本機(jī)內(nèi)存分配的常見來源,現(xiàn)在是時候找出如何監(jiān)視它們了。首先,我們應(yīng)該使用另一個JVM調(diào)優(yōu)標(biāo)志啟用本機(jī)內(nèi)存跟蹤:-XX:NativeMemoryTracking = off | sumary | detail。默認(rèn)情況下,NMT處于關(guān)閉狀態(tài),但我們可以使其查看其觀察的摘要或詳細(xì)視圖。

    假設(shè)我們想要跟蹤典型Spring Boot應(yīng)用程序的本機(jī)分配:

    $ java -XX:NativeMemoryTracking=summary -Xms300m -Xmx300m -XX:+UseG1GC -jar app.jar

    在這里,我們在分配300 MB堆空間的同時啟用NMT,G1作為我們的GC算法。

    3.1. 實例快照

    啟用NMT后,我們可以使用jcmd命令隨時獲取本機(jī)內(nèi)存信息:

    $ jcmd <pid> VM.native_memory

    為了找到JVM應(yīng)用程序的PID,我們可以使用jps命令:

    $ jps -l                    
    7858 app.jar // This is our app
    7899 sun.tools.jps.Jps

    現(xiàn)在,如果我們將jcmd與適當(dāng)?shù)膒id一起使用,VM.native_memory會使JVM打印出有關(guān)本機(jī)分配的信息:

    $ jcmd 7858 VM.native_memory

    讓我們逐節(jié)分析NMT輸出。

    3.2. 總分配

    NMT報告全部保留和提交的內(nèi)存如下:

    Native Memory Tracking:
    Total: reserved=1731124KB, committed=448152KB

    保留內(nèi)存表示我們的應(yīng)用程序可能使用的內(nèi)存總量。相反,提交的內(nèi)存表示我們的應(yīng)用程序現(xiàn)在使用的內(nèi)存量。

    盡管分配了300MB的堆,我們的應(yīng)用程序的總預(yù)留內(nèi)存幾乎是1.7 GB,遠(yuǎn)遠(yuǎn)超過它。類似地,提交的內(nèi)存大約為440 MB,這再次遠(yuǎn)遠(yuǎn)超過300 MB。

    在整體了解之后,NMT報告每個分配源的內(nèi)存分配。所以,讓我們深入探討每個來源。

    3.3. Heap(堆)

    NMT按我們的預(yù)期報告堆分配:

    Java Heap (reserved=307200KB, committed=307200KB)
            (mmap: reserved=307200KB, committed=307200KB)

    300 MB的保留和已提交內(nèi)存,與我們的堆大小設(shè)置相匹配。

    3.4. Metaspace(元空間)

    這是NMT關(guān)于加載類的元數(shù)據(jù)的報告:

    Class (reserved=1091407KB, committed=45815KB)
        (classes #6566)
        (malloc=10063KB #8519) 
        (mmap: reserved=1081344KB, committed=35752KB)

    幾乎保留了1 GB,45 MB保留加載6566個類。

    3.5. Thread(線程)

    這是關(guān)于線程分配的NMT報告:

    Thread (reserved=37018KB, committed=37018KB)
         (thread #37)
         (stack: reserved=36864KB, committed=36864KB)
         (malloc=112KB #190) 
         (arena=42KB #72)

    總共有36 MB的內(nèi)存被分配給37個線程的堆棧 - 每個堆棧大約1 MB。 JVM在創(chuàng)建時將內(nèi)存分配給線程,因此保留和提交的分配是相等的。

    3.6. Code Cache(代碼緩沖區(qū))

    讓我們看看NMT對JIT生成和緩存的匯編指令的報告:

    Code (reserved=251549KB, committed=14169KB)
       (malloc=1949KB #3424) 
       (mmap: reserved=249600KB, committed=12220KB)

    目前,正在緩存大約13 MB的代碼,這個數(shù)量可能會達(dá)到245 MB。

    3.7. GC

    以下是有關(guān)G1 GC內(nèi)存使用情況的NMT報告:

    GC (reserved=61771KB, committed=61771KB)
     (malloc=17603KB #4501) 
     (mmap: reserved=44168KB, committed=44168KB)

    我們可以看到,保留和已提交都接近60 MB,致力于幫助G1。

    讓我們來看看更簡單的GC的內(nèi)存使用情況,比如Serial GC:

    $ java -XX:NativeMemoryTracking=summary -Xms300m -Xmx300m -XX:+UseSerialGC -jar app.jar

    Serial GC 幾乎使用不到1 MB:

    GC (reserved=1034KB, committed=1034KB)
     (malloc=26KB #158) 
     (mmap: reserved=1008KB, committed=1008KB)

    顯然,我們不能僅僅因為其內(nèi)存使用而選擇GC算法,因為串行GC的暫?;厥毡举|(zhì)可能會導(dǎo)致性能下降。但是,還有幾個GC可供選擇,它們各自平衡內(nèi)存和性能。

    3.8. Symbol(符號)

    以下是有關(guān)符號分配的NMT報告,例如字符串表和常量池:

    Symbol (reserved=10148KB, committed=10148KB)
         (malloc=7295KB #66194) 
         (arena=2853KB #1)

    將近10 MB分配給符號。

    3.9. 隨著時間的推移的NMT

    NMT允許我們跟蹤內(nèi)存分配如何隨時間變化。首先,我們應(yīng)該將應(yīng)用程序的當(dāng)前狀態(tài)標(biāo)記為基線:

    $ jcmd <pid> VM.native_memory baseline
    Baseline succeeded

    然后,過了一會兒,我們可以將當(dāng)前的內(nèi)存使用情況與該基線(baseline)進(jìn)行比較:

    $ jcmd <pid> VM.native_memory summary.diff

    NMT使用+和 - 符號將告訴我們在此期間內(nèi)存使用情況如何變化:

    
    Total: reserved=1771487KB +3373KB, committed=491491KB +6873KB
  • Java Heap (reserved=307200KB, committed=307200KB)
    (mmap: reserved=307200KB, committed=307200KB)

  • Class (reserved=1084300KB +2103KB, committed=39356KB +2871KB)
    // Truncated

    
    
    保留和提交的總內(nèi)存分別增加了3 MB和6 MB??梢院苋菀椎匕l(fā)現(xiàn)內(nèi)存分配的其他波動。
    
    ### 3.10. 詳細(xì)的NMT
    
    NMT可以提供非常詳細(xì)的有關(guān)整個存儲空間映射的信息。要啟用此詳細(xì)報告,我們應(yīng)使用 `-XX:NativeMemoryTracking =detail` 信息調(diào)整標(biāo)志。
    
    ## 4. 結(jié)束語
    
    在本文中,我們列舉了JVM中本機(jī)內(nèi)存分配的不同使用者。然后,我們學(xué)習(xí)了如何檢查正在運(yùn)行的應(yīng)用程序以監(jiān)視其本機(jī)分配。借助以上這些,我們可以更有效地調(diào)整應(yīng)用程序以及運(yùn)行時環(huán)境的大小。
    
    > 原文:<https://www.baeldung.com/native-memory-tracking-in-jvm>
    >
    > 作者:[Ali Dehghani](https://www.baeldung.com/author/ali-dehghani/)
    >
    > 譯者:Emma

文章題目:追蹤JVM中的本地內(nèi)存
URL鏈接:http://bm7419.com/article0/jdeeoo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供軟件開發(fā)、網(wǎng)站維護(hù)、小程序開發(fā)、網(wǎng)站策劃、品牌網(wǎng)站建設(shè)、微信公眾號

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都seo排名網(wǎng)站優(yōu)化