JVM優化經驗總結

Mondo 教育 更新 2024-02-08

通過示例和相應的輸出說明,您將對 JVM 優化有乙個初步的了解。

j**a虛擬機器有自己完整的硬體架構,如處理器、堆疊、暫存器等,也有相應的指令系統。 JVM 遮蔽了有關特定作業系統平台的資訊,因此只需生成在 JVM 上執行的目標位元組碼,JA 程式就可以在各種平台上執行,而無需修改。 當 j**a 虛擬機器執行位元組碼時,它實際上最終會將位元組碼解釋為在特定平台上執行機器指令。

注意:本文僅重點介紹 JDK7 和 Hotspot J**A VM,不重點介紹 JDK8 和其他 J**A VM 中引入的新 JVM 功能。

讓我們從示例開始本文。 假設你是乙個普通的j**a物件,你出生在伊甸園區,伊甸園區有很多和你長相相似的小弟弟妹妹,你可以把伊甸園區當成幼兒園,大家在這個幼兒園裡玩了很久。 伊甸園區不能無休止地呆在裡面,所以等你長大一點,你就會被送到學校,從小學到高中都叫倖存者區。

剛開始,你在倖存者區劃分了從區,到了大四的時候,你進入了倖存者區的到區,由於中間學習成績不穩定,你經常來回折騰。 當你18歲的時候,你已經高中畢業了,是時候在社會上闖出一片天了。所以你去找老一代,老一代有很多人。 在上一輩,你活了20年(每個GC加一歲),最後你死了,而GC**根本沒有提,你遇到了乙個老一輩的同學,他的名字叫愛德華(光之城的帥氣吸血鬼),他和他的家人永遠不會死,然後他們生活在永恆時代。

在上一篇文章“JVM Junkter 的工作原理和用例簡介”中,我們介紹了年輕一代、老年人和不朽一代,本文主要討論如何利用這些領域為系統效能提供更好的幫助。 本文不重複這些概念,讓我們直奔主題。 眾所周知,由於全GC的成本遠高於次要GC,因此在某些情況下,有必要盡可能地將物件分配給年輕一代,這在很多情況下都是明智的選擇。 雖然在大多數情況下,JVM 會嘗試在 eden zone 中分配物件,但由於空間限制和其他問題,很可能不得不提前將一些較年輕的物件壓縮到老一代。 因此,在調整JVM引數時,可以為應用分配合理的年輕一代空間,最大程度避免新物件直接進入老一代的發生。 如清單 1** 所示,嘗試分配 4MB 的記憶體空間並觀察其記憶體使用情況。

清單 1相同大小的記憶體分配。

public class putineden }
使用 jvm 引數 -xx:+printgcdetails -xmx20m -xms20m 執行清單 1,輸出如清單 2 所示。

清單 2清單 1 執行輸出。

[gc [defnew: 5504k->640k(6144k), 0.0114236 secs] 5504k->5352k(19840k),0.0114595 secs] [times: user=0.02 sys=0.00, real=0.02 secs][gc [defnew: 6144k->640k(6144k), 0.0131261 secs] 10856k->10782k(19840k),0.0131612 secs] [times: user=0.02 sys=0.00, real=0.02 secs][gc [defnew: 6144k->6144k(6144k), 0.0000170 secs][tenured: 10142k->13695k(13696k),0.1069249 secs] 16286k->15966k(19840k), perm : 376k->376k(12288k)],0.1070058 secs] [times: user=0.03 sys=0.00, real=0.11 secs][full gc [tenured: 13695k->13695k(13696k), 0.0302067 secs] 19839k->19595k(19840k),[perm : 376k->376k(12288k)],0.0302635 secs] [times: user=0.03 sys=0.00, real=0.03 secs][full gc [tenured: 13695k->13695k(13696k), 0.0311986 secs] 19839k->19839k(19840k),[perm : 376k->376k(12288k)],0.0312515 secs] [times: user=0.03 sys=0.00, real=0.03 secs][full gc [tenured: 13695k->13695k(13696k), 0.0358821 secs] 19839k->19825k(19840k),[perm : 376k->371k(12288k)],0.0359315 secs] [times: user=0.05 sys=0.00, real=0.05 secs][full gc [tenured: 13695k->13695k(13696k), 0.0283080 secs] 19839k->19839k(19840k),[perm : 371k->371k(12288k)],0.0283723 secs] [times: user=0.02 sys=0.00, real=0.01 secs][full gc [tenured: 13695k->13695k(13696k), 0.0284469 secs] 19839k->19839k(19840k),[perm : 371k->371k(12288k)],0.0284990 secs] [times: user=0.03 sys=0.00, real=0.03 secs][full gc [tenured: 13695k->13695k(13696k), 0.0283005 secs] 19839k->19839k(19840k),[perm : 371k->371k(12288k)],0.0283475 secs] [times: user=0.03 sys=0.00, real=0.03 secs][full gc [tenured: 13695k->13695k(13696k), 0.0287757 secs] 19839k->19839k(19840k),[perm : 371k->371k(12288k)],0.0288294 secs] [times: user=0.03 sys=0.00, real=0.03 secs][full gc [tenured: 13695k->13695k(13696k), 0.0288219 secs] 19839k->19839k(19840k),[perm : 371k->371k(12288k)],0.0288709 secs] [times: user=0.03 sys=0.00, real=0.03 secs][full gc [tenured: 13695k->13695k(13696k), 0.0293071 secs] 19839k->19839k(19840k),[perm : 371k->371k(12288k)],0.0293607 secs] [times: user=0.03 sys=0.00, real=0.03 secs][full gc [tenured: 13695k->13695k(13696k), 0.0356141 secs] 19839k->19838k(19840k),[perm : 371k->371k(12288k)],0.0356654 secs] [times: user=0.01 sys=0.00, real=0.03 secs]heapdef new generation total 6144k, used 6143k [0x35c10000, 0x362b0000, 0x362b0000)eden space 5504k, 100% used [0x35c10000, 0x36170000, 0x36170000)from space 640k, 99% used [0x36170000, 0x3620fc80, 0x36210000)to space 640k, 0% used [0x36210000, 0x36210000, 0x362b0000)tenured generation total 13696k, used 13695k [0x362b0000, 0x37010000, 0x37010000)the space 13696k, 99% used [0x362b0000, 0x3700fff8, 0x37010000, 0x37010000)compacting perm gen total 12288k, used 371k [0x37010000, 0x37c10000, 0x3b010000)the space 12288k, 3% used [0x37010000, 0x3706cd20, 0x3706ce00, 0x37c10000)ro space 10240k, 51% used [0x3b010000, 0x3b543000, 0x3b543000, 0x3ba10000)rw space 12288k, 55% used [0x3ba10000, 0x3c0ae4f8, 0x3c0ae600, 0x3c610000)
清單 2 中所示的日誌輸出顯示,年輕一代的 Eden 大小約為 5MB。 使用 jvm 引數 -xx:+printgcDetails -xmx20m -xms20m-xmn6m 分配足夠的年輕一代空間來執行清單 1,輸出如清單 3 所示。

清單 3清單 1 在增加 eden 大小後執行輸出。

[gc [defnew: 4992k->576k(5568k), 0.0116036 secs] 4992k->4829k(19904k),0.0116439 secs] [times: user=0.02 sys=0.00, real=0.02 secs][gc [defnew: 5568k->576k(5568k), 0.0130929 secs] 9821k->9653k(19904k),0.0131336 secs] [times: user=0.02 sys=0.00, real=0.02 secs][gc [defnew: 5568k->575k(5568k), 0.0154148 secs] 14645k->14500k(19904k),0.0154531 secs] [times: user=0.00 sys=0.01, real=0.01 secs][gc [defnew: 5567k->5567k(5568k), 0.0000197 secs][tenured: 13924k->14335k(14336k),0.0330724 secs] 19492k->19265k(19904k), perm : 376k->376k(12288k)],0.0331624 secs] [times: user=0.03 sys=0.00, real=0.03 secs][full gc [tenured: 14335k->14335k(14336k), 0.0292459 secs] 19903k->19902k(19904k),[perm : 376k->376k(12288k)],0.0293000 secs] [times: user=0.03 sys=0.00, real=0.03 secs][full gc [tenured: 14335k->14335k(14336k), 0.0278675 secs] 19903k->19903k(19904k),[perm : 376k->376k(12288k)],0.0279215 secs] [times: user=0.03 sys=0.00, real=0.03 secs][full gc [tenured: 14335k->14335k(14336k), 0.0348408 secs] 19903k->19889k(19904k),[perm : 376k->371k(12288k)],0.0348945 secs] [times: user=0.05 sys=0.00, real=0.05 secs][full gc [tenured: 14335k->14335k(14336k), 0.0299813 secs] 19903k->19903k(19904k),[perm : 371k->371k(12288k)],0.0300349 secs] [times: user=0.01 sys=0.00, real=0.02 secs][full gc [tenured: 14335k->14335k(14336k), 0.0298178 secs] 19903k->19903k(19904k),[perm : 371k->371k(12288k)],0.0298688 secs] [times: user=0.03 sys=0.00, real=0.03 secs]exception in thread "main" j**a.lang.outofmemoryerror: j**a heap space[full gc [tenured:14335k->14335k(14336k), 0.0294953 secs] 19903k->19903k(19904k),[perm : 371k->371k(12288k)],0.0295474 secs] [times: user=0.03 sys=0.00, real=0.03 secs][full gc [tenured: 14335k->14335k(14336k), 0.0287742 secs] 19903k->19903k(19904k),[perm : 371k->371k(12288k)],0.0288239 secs] [times: user=0.03 sys=0.00, real=0.03 secs][full gc [tenuredat gctimetest.main(gctimetest.j**a:16): 14335k->14335k(14336k), 0.0287102 secs] 19903k->19903k(19904k),[perm : 371k->371k(12288k)],0.0287627 secs] [times: user=0.03 sys=0.00, real=0.03 secs]heapdef new generation total 5568k, used 5567k [0x35c10000, 0x36210000, 0x36210000)eden space 4992k, 100% used [0x35c10000, 0x360f0000, 0x360f0000)from space 576k, 99% used [0x36180000, 0x3620ffe8, 0x36210000)to space 576k, 0% used [0x360f0000, 0x360f0000, 0x36180000)tenured generation total 14336k, used 14335k [0x36210000, 0x37010000, 0x37010000)the space 14336k, 99% used [0x36210000, 0x3700ffd8, 0x37010000, 0x37010000)compacting perm gen total 12288k, used 371k [0x37010000, 0x37c10000, 0x3b010000)the space 12288k, 3% used [0x37010000, 0x3706ce28, 0x3706d000, 0x37c10000)ro space 10240k, 51% used [0x3b010000, 0x3b543000, 0x3b543000, 0x3ba10000)rw space 12288k, 55% used [0x3ba10000, 0x3c0ae4f8, 0x3c0ae600, 0x3c610000)
通過對清單2和清單3的比較,可以發現,通過設定大量年輕一代來保留新物件,設定合理的倖存者區域,並提供倖存者區域的利用率,可以將年輕物件儲存在年輕一代中。 一般來說,倖存者區空間不足,或者當達到50%的入住率時,將導致受試者進入老年(無論其年齡大小)。 清單 4 建立了 3 個物件,每個物件都有一定的記憶體空間。

清單 4不同大小的記憶體分配。

public class putineden2 }
使用引數 -xx:+printgcDetails -xmx1000m -xms500m -xmn100m -xx:survivorratio=8 執行清單 4,輸出如清單 5 所示。

清單 5清單 4 執行輸出。

heapdef new generation total 92160k, used 11878k [0x0f010000, 0x15410000, 0x15410000)eden space 81920k, 2% used [0x0f010000, 0x0f1a9a20, 0x14010000)from space 10240k, 99% used [0x14a10000, 0x1540fff8, 0x15410000)to space 10240k, 0% used [0x14010000, 0x14010000, 0x14a10000)tenured generation total 409600k, used 86434k [0x15410000, 0x2e410000, 0x4d810000)the space 409600k, 21% used [0x15410000, 0x1a878b18, 0x1a878c00, 0x2e410000)compacting perm gen total 12288k, used 2062k [0x4d810000, 0x4e410000, 0x51810000)the space 12288k, 16% used [0x4d810000, 0x4da13b18, 0x4da13c00, 0x4e410000)no shared spaces configured.
清單 5 的對數輸出顯示,年輕一代被分配了 8m,老一代也被分配了 8m。 我們可以嘗試新增 -xx:targetsurvivorratio=90 引數,這樣可以提高 from zone 的利用率,這樣當 from zone 被使用到 90% 時,然後將物件傳送給老一代,並執行 Manifest 4 ** 輸出如清單 6 所示。

清單 6修改執行引數後的清單 4 輸出。

heapdef new generation total 9216k, used 9215k [0x35c10000, 0x36610000, 0x36610000)eden space 8192k, 100% used [0x35c10000, 0x36410000, 0x36410000)from space 1024k, 99% used [0x36510000, 0x3660fc50, 0x36610000)to space 1024k, 0% used [0x36410000, 0x36410000, 0x36510000)tenured generation total 10240k, used 10239k [0x36610000, 0x37010000, 0x37010000)the space 10240k, 99% used [0x36610000, 0x3700ff70, 0x37010000, 0x37010000)compacting perm gen total 12288k, used 371k [0x37010000, 0x37c10000, 0x3b010000)the space 12288k, 3% used [0x37010000, 0x3706cd90, 0x3706ce00, 0x37c10000)ro space 10240k, 51% used [0x3b010000, 0x3b543000, 0x3b543000, 0x3ba10000)rw space 12288k, 55% used [0x3ba10000, 0x3c0ae4f8, 0x3c0ae600, 0x3c610000)
如果存活率設定為 2,則 B1 物件在年輕一代中預先存在。 輸出如清單 7 所示。

清單 7再次修改執行引數後,將顯示清單 4 的輸出。

heapdef new generation total 7680k, used 7679k [0x35c10000, 0x36610000, 0x36610000)eden space 5120k, 100% used [0x35c10000, 0x36110000, 0x36110000)from space 2560k, 99% used [0x36110000, 0x3638fff0, 0x36390000)to space 2560k, 0% used [0x36390000, 0x36390000, 0x36610000)tenured generation total 10240k, used 10239k [0x36610000, 0x37010000, 0x37010000)the space 10240k, 99% used [0x36610000, 0x3700fff0, 0x37010000, 0x37010000)compacting perm gen total 12288k, used 371k [0x37010000, 0x37c10000, 0x3b010000)the space 12288k, 3% used [0x37010000, 0x3706ce28, 0x3706d000, 0x37c10000)ro space 10240k, 51% used [0x3b010000, 0x3b543000, 0x3b543000, 0x3ba10000)rw space 12288k, 55% used [0x3ba10000, 0x3c0ae4f8, 0x3c0ae600, 0x3c610000)
在大多數情況下,我們選擇將物件分配給年輕一代。 但是,對於大型記憶體密集型物件,情況可能並非如此。 因為年輕一代中大型物體的存在,很可能會擾亂年輕一代的GC,破壞年輕一代原有的物體結構。 因為嘗試將大型物件分配給年輕一代可能會導致空間不足,因此 JVM 必須將年輕一代中的年輕物件移動到老一代,以便有足夠的空間來容納大型物件。 由於大型物體占用大量空間,因此可能需要將大量小的幼小物體移動到老一輩,這對GC是相當不利的。

由於這些原因,可以將大型物件直接分配給老一輩,並保持年輕一代物件結構的完整性,從而提高GC的效率。 如果乙個大物件也是乙個短暫的物件,假設這種情況經常發生,這對 GC 來說將是一場災難。 本來應該用來儲存永久物品的老一代,卻塞滿了短命的物品,這也意味著堆空間被洗牌,打亂了世代記憶的基本理念。

因此,在軟體開發過程中,應盡可能避免使用壽命較短的大物件。 您可以使用 -xx:petenuresizethreshold 引數來設定大型物件的閾值,使其直接轉到老一代。 當物件的大小超過此值時,將直接在舊一代中分配。 引數 -xx:petenuresizethreshold 僅對串列埠採集器和年輕一代並行採集器有效,並行**採集器無法識別該引數。

清單 8建立乙個大型物件。

public class bigobj2old }
使用 jvm 引數 -xx:+printgcdetails xmx20m xms20mb 執行,將得到清單 9 所示的日誌輸出。

清單 9清單 8 執行輸出。

heapdef new generation total 6144k, used 1378k [0x35c10000, 0x362b0000, 0x362b0000)eden space 5504k, 25% used [0x35c10000, 0x35d689e8, 0x36170000)from space 640k, 0% used [0x36170000, 0x36170000, 0x36210000)to space 640k, 0% used [0x36210000, 0x36210000, 0x362b0000)tenured generation total 13696k, used 0k [0x362b0000, 0x37010000, 0x37010000)the space 13696k, 0% used [0x362b0000, 0x362b0000, 0x362b0200, 0x37010000)compacting perm gen total 12288k, used 374k [0x37010000, 0x37c10000, 0x3b010000)the space 12288k, 3% used [0x37010000, 0x3706dac8, 0x3706dc00, 0x37c10000)ro space 10240k, 51% used [0x3b010000, 0x3b543000, 0x3b543000, 0x3ba10000)rw space 12288k, 55% used [0x3ba10000, 0x3c0ae4f8, 0x3c0ae600, 0x3c610000)
可以看出,該物件分配給了年輕一代,佔據了25%的空間。 如果要將大於 1MB 的物件直接分配給老年人,請設定 -xx:petenuresizethreshold=1000000,程式執行後,輸出將顯示在清單 10 中。

清單 10修改執行引數後的清單 8 輸出。

heapdef new generation total 6144k, used 354k [0x35c10000, 0x362b0000, 0x362b0000)eden space 5504k, 6% used [0x35c10000, 0x35c689d8, 0x36170000)from space 640k, 0% used [0x36170000, 0x36170000, 0x36210000)to space 640k, 0% used [0x36210000, 0x36210000, 0x362b0000)tenured generation total 13696k, used 1024k [0x362b0000, 0x37010000, 0x37010000)the space 13696k, 7% used [0x362b0000, 0x363b0010, 0x363b0200, 0x37010000)compacting perm gen total 12288k, used 374k [0x37010000, 0x37c10000, 0x3b010000)the space 12288k, 3% used [0x37010000, 0x3706dac8, 0x3706dc00, 0x37c10000)ro space 10240k, 51% used [0x3b010000, 0x3b543000, 0x3b543000, 0x3ba10000)rw space 12288k, 55% used [0x3ba10000, 0x3c0ae4f8, 0x3c0ae600, 0x3c610000)
在清單 10 中,您可以看到,當 1MB 已滿時,它進入了高階一代。

堆中的每個物件都有自己的期限。 一般來說,年輕的物件儲存在年輕一代中,而舊的物件儲存在老一代中。 為此,虛擬機會為每個物件保留乙個期限。 如果物件在伊甸園區域,並且在GC後還活著,則將其移動到倖存者區域,並將物件的年齡加1。 之後,如果受試者在每次 GC 經歷中倖存下來,則年齡會額外增加 1。 當主體的年齡達到門檻時,它就進入了老年一代,成為老年客體。 可以使用引數 -xx:maxtenuringthreshold 設定此閾值的最大值,預設值為 15。

雖然 -xx:maxtenuringthreshold 的值可能為 15 或更大,但這並不意味著新物件必須達到這個年齡才能進入老一代。 實際上,物件實際進入老一代的期限是由虛擬機器在執行時根據記憶體使用情況動態計算的,此引數指定閾值期限的最大值。 也就是說,晉公升的老一輩的實際年齡等於動態計算的年齡和 -xx:maxtenuringthreshold 中的較小者。 清單 11 顯示了 3 個物件宣告了多個記憶體。

清單 11請求記憶體。

public class maxtenuringthreshold }
引數設定為:-xx:+printgcdetails -xmx20m -xms20m -xmn10m -xx:survivorratio=2

執行清單 11,輸出如清單 12 所示。

清單 12清單 11 執行輸出。

[gc [defnew: 2986k->690k(7680k), 0.0246816 secs] 2986k->2738k(17920k),0.0247226 secs] [times: user=0.00 sys=0.02, real=0.03 secs][gc [defnew: 4786k->690k(7680k), 0.0016073 secs] 6834k->2738k(17920k),0.0016436 secs] [times: user=0.00 sys=0.00, real=0.00 secs]heapdef new generation total 7680k, used 4888k [0x35c10000, 0x36610000, 0x36610000)eden space 5120k, 82% used [0x35c10000, 0x36029a18, 0x36110000)from space 2560k, 26% used [0x36110000, 0x361bc950, 0x36390000)to space 2560k, 0% used [0x36390000, 0x36390000, 0x36610000)tenured generation total 10240k, used 2048k [0x36610000, 0x37010000, 0x37010000)the space 10240k, 20% used [0x36610000, 0x36810010, 0x36810200, 0x37010000)compacting perm gen total 12288k, used 374k [0x37010000, 0x37c10000, 0x3b010000)the space 12288k, 3% used [0x37010000, 0x3706db50, 0x3706dc00, 0x37c10000)ro space 10240k, 51% used [0x3b010000, 0x3b543000, 0x3b543000, 0x3ba10000)rw space 12288k, 55% used [0x3ba10000, 0x3c0ae4f8, 0x3c0ae600, 0x3c610000)
將引數更改為 -xx:+printgcdetails -xmx20m -xms20m -xmn10m -xx:survivorratio=2 -xx:maxtenuringthreshold=1,執行清單 11 **,輸出如清單 13 所示。

清單 13修改執行引數後的 Manifest 11 輸出。

[gc [defnew: 2986k->690k(7680k), 0.0047778 secs] 2986k->2738k(17920k),0.0048161 secs] [times: user=0.00 sys=0.00, real=0.00 secs][gc [defnew: 4888k->0k(7680k), 0.0016271 secs] 6936k->2738k(17920k),0.0016630 secs] [times: user=0.00 sys=0.00, real=0.00 secs]heapdef new generation total 7680k, used 4198k [0x35c10000, 0x36610000, 0x36610000)eden space 5120k, 82% used [0x35c10000, 0x36029a18, 0x36110000)from space 2560k, 0% used [0x36110000, 0x36110088, 0x36390000)to space 2560k, 0% used [0x36390000, 0x36390000, 0x36610000)tenured generation total 10240k, used 2738k [0x36610000, 0x37010000, 0x37010000)the space 10240k, 26% used [0x36610000, 0x368bc890, 0x368bca00, 0x37010000)compacting perm gen total 12288k, used 374k [0x37010000, 0x37c10000, 0x3b010000)the space 12288k, 3% used [0x37010000, 0x3706db50, 0x3706dc00, 0x37c10000)ro space 10240k, 51% used [0x3b010000, 0x3b543000, 0x3b543000, 0x3ba10000)rw space 12288k, 55% used [0x3ba10000, 0x3c0ae4f8, 0x3c0ae600, 0x3c610000)
如清單 13 所示,在程式首次執行時,程式完成後,b1 物件仍儲存在年輕一代中。 在第二次執行之前,我們將專案晉公升為長老的年齡降低到 1。 也就是說,所有通過GC一次的物件都可以直接轉到老一代。 程式執行後,可以發現 B1 物件已經分配給了老一代。 如果你想讓物件盡可能長時間地留在年輕一代,你可以設定乙個大的閾值。

通常,穩定的堆大小對垃圾有好處**。 獲得穩定堆大小的方法是使 -xms 和 -xmx 的大小相同,即最大堆與最小堆(初始堆)相同。 如果以這種方式設定,理論上堆大小在執行時是恆定的,穩定的堆空間可以減少 GC 的數量。 因此,許多伺服器端應用程式將最大和最小堆設定為相同的值。

但是,不穩定的堆並非毫無用處。 穩定的堆大小可以減少 GC 的數量,但也會增加每個 GC 的時間。 將堆大小放在區域**中可以通過壓縮堆空間來加快單個 GC 的速度,以便 GC 可以在系統不需要使用大記憶體時處理較小的堆。 考慮到這一點,JVM 還提供了兩個用於壓縮和擴充套件堆空間的引數。

xx:minheapfreeratio 引數用於設定堆空間的最小空閒率,預設值為 40。 當堆空間的可用記憶體小於此值時,JVM 會擴充套件堆空間。

xx:maxheapfreeratio 引數用於設定堆空間的最大空閒比率,預設值為 70。 當堆空間的可用記憶體大於此值時,將壓縮堆空間以獲得更小的堆。

當 -xmx 和 -xms 相等時,-xx:minheapfreeratio 和 -xx:maxheapfreeratio 引數無效。

清單 14堆大小設定。

import j**a.util.vector;public class heapsize thread.sleep(1);}
清單 14 顯示了測試 -xx:minheapfreeratio 和 -xx:maxheapfreeratio 的效果,當執行引數為 -xx:+printgcdetails -xms10m -xmx40m -xx:minheapfreeratio=40 -xx:maxheapfreeratio =50 時,輸出如清單 15 所示。

清單 15修改執行引數後的清單 14 輸出。

[gc [defnew: 2418k->178k(3072k), 0.0034827 secs] 2418k->2226k(9920k),0.0035249 secs] [times: user=0.00 sys=0.00, real=0.03 secs][gc [defnew: 2312k->0k(3072k), 0.0028263 secs] 4360k->4274k(9920k),0.0029905 secs] [times: user=0.00 sys=0.00, real=0.03 secs][gc [defnew: 2068k->0k(3072k), 0.0024363 secs] 6342k->6322k(9920k),0.0024836 secs] [times: user=0.00 sys=0.00, real=0.03 secs][gc [defnew: 2061k->0k(3072k), 0.0017376 secs][tenured: 8370k->8370k(8904k),0.1392692 secs] 8384k->8370k(11976k), perm : 374k->374k(12288k)],0.1411363 secs] [times: user=0.00 sys=0.02, real=0.16 secs][gc [defnew: 5138k->0k(6336k), 0.0038237 secs] 13508k->13490k(20288k),0.0038632 secs] [times: user=0.00 sys=0.00, real=0.03 secs]
使用引數 -xx:+printgcdetails -xms40m -xmx40m -xx:minheapfreeratio=40 -xx:maxheapfreeratio=50,輸出如清單 16 所示。

清單 16再次修改執行引數後的清單 14 輸出。

[gc [defnew: 10678k->178k(12288k), 0.0019448 secs] 10678k->178k(39616k),0.0019851 secs] [times: user=0.00 sys=0.00, real=0.03 secs][gc [defnew: 10751k->178k(12288k), 0.0010295 secs] 10751k->178k(39616k),0.0010697 secs] [times: user=0.00 sys=0.00, real=0.02 secs][gc [defnew: 10493k->178k(12288k), 0.0008301 secs] 10493k->178k(39616k),0.0008672 secs] [times: user=0.00 sys=0.00, real=0.02 secs][gc [defnew: 10467k->178k(12288k), 0.0008522 secs] 10467k->178k(39616k),0.0008905 secs] [times: user=0.00 sys=0.00, real=0.02 secs][gc [defnew: 10450k->178k(12288k), 0.0008964 secs] 10450k->178k(39616k),0.0009339 secs] [times: user=0.00 sys=0.00, real=0.01 secs][gc [defnew: 10439k->178k(12288k), 0.0009876 secs] 10439k->178k(39616k),0.0010279 secs] [times: user=0.00 sys=0.00, real=0.02 secs]
從清單 16 中可以看出,堆空間中的垃圾在固定範圍內是穩定的。 在穩定堆中,堆大小始終相同,每次使用 GC 時,都會協調乙個 40MB 的空間。 因此,雖然減少了 GC 的數量,但單個 GC 的速度不如堆的速度。

吞吐量優先方法將最大限度地減少系統執行垃圾所需的總時間,因此請考慮關注系統吞吐量的並行收集器。 在高效能計算機上,可以使用以下引數執行吞吐量優先優化:

j**a –xmx3800m –xms3800m –xmn2g –xss128k –xx:+useparallelgc–xx:parallelgc-threads=20 –xx:+useparalleloldgc
XMX3800M XMS3800M:設定 j**a 堆的最大值和初始值。 通常,為了避免頻繁的堆記憶體和降低系統效能,我們將最大堆設定為等於最小堆。 假設最小堆減少到最大堆的一半,即 1900M,那麼 JVM 將盡可能在 1900MB 的堆空間中執行,如果是這樣,GC 發生的概率就更高了;

XSS128K:減小執行緒堆疊的大小,允許剩餘的系統記憶體支援更多的執行緒;

XMN2G:將年輕一代區域的大小設定為 2GB;

xx:+useparallelgc:年輕一代使用並行垃圾回收器。 這是乙個以吞吐量為中心的收集器,可最大限度地減少 GC 時間。

xx:parallelgc-threads:設定用於垃圾的執行緒數**,通常可以設定為等於 CPU 的數量。 但是,在CPU數量較多的情況下,設定乙個相對較小的值是合理的;

xx:+useparalleloldgc:設定舊一代使用的收集器。

CPU 通過定址訪問記憶體。 32位CPU的定址寬度為0 0xffffffff,計算大小為4G,這意味著可以支援的最大物理記憶體量為4G。 但是,在實踐中,我們遇到了這樣的問題,程式需要使用4G記憶體,而可用的物理記憶體小於4G,導致程式不得不減少記憶體占用。

為了解決這些問題,現代CPU引入了記憶體管理單元(Memory Management Unit)。 MMU的核心思想是使用虛擬位址而不是實體地址,即CPU使用虛擬位址進行定址,MMU負責將虛擬位址對映到實體地址。 MMU的引入解決了物理記憶體的侷限性,對於程式來說,就像使用4G記憶體一樣。

記憶體分頁是在使用 MMU 的基礎上提出的一種記憶體管理機制。 它將虛擬位址和實體地址拆分為固定大小 (4k) 的頁面和頁面框架,並保證頁面的大小與頁面框架相同。 就資料結構而言,這種機制確保了對記憶體的有效訪問,並使作業系統能夠支援非連續的記憶體分配。 當程式記憶體不足時,還可以將不常用的物理記憶體頁轉移到其他儲存裝置,例如磁碟,這稱為虛擬記憶體。

在 Solaris 系統中,JVM 可以支援使用較大的頁面大小。 使用大記憶體分頁可以增強 CPU 的記憶體定址能力,從而提高系統的效能。

j**a –xmx2506m –xms2506m –xmn1536m –xss128k –xx:++useparallelgc–xx:parallelgcthreads=20 –xx:+useparalleloldgc –xx:+largepagesizeinbytes=256m
xx:+largepagesizeInbytes:指定大頁面的大小。

在計算 HEAP 內部分割槽(perm、new、old)的記憶體占用時,過多的記憶體分頁會導致 JVM 分割槽超出正常範圍,在最壞的情況下,區域將占用額外的頁面大小。

為了減少應用垃圾郵件的暫停,首先要考慮的是使用乙個專注於系統暫停的CMS,其次,為了減少全GC的數量,物件應該盡可能地留給年輕一代,因為年輕一代的小GC的成本遠小於老一代的全GC。

j**a –xmx3550m –xms3550m –xmn2g –xss128k –xx:parallelgcthreads=20–xx:+useconcmarksweepgc –xx:+useparnewgc –xx:+survivorratio=8 –xx:targetsurvivorratio=90–xx:maxtenuringthreshold=31
xx:parallelgcthreads=20:設定 20 個執行緒作為垃圾**;

xx: +useparnewgc: 年輕一代使用並行器;

xx:+useconcmarksweepgc:老一輩使用 CMS 收集器來減少暫停;

xx:+survivorratio:將伊甸園區與倖存者區的比例設定為8:1。 稍大一點的倖存者空間會增加年輕一代壽命較短的物體的可能性**,如果倖存者不夠大,一些短命的物體可能會直接進入老一輩,這對系統是不利的。

xx:targetsurvivorratio=90:設定倖存者區域的可用率。 如果設定為 90%,則允許使用 90% 的倖存者空間。 預設值為 50%。 因此,此設定會提高倖存者區域的使用率。 當儲存的物件數超過此百分比時,這些物件將被壓縮到老一代。 因此,此選項更有助於將物件保留在年輕一代中。

xx:maxtenuringthreshold:設定年輕受試者晉公升為老一輩的年齡。 預設值為 15 次,即如果受試者存活了 15 次,則進入老一代。 此值設定為 31,以便物件盡可能保留在年輕一代區域。

通過對本文的學習,讀者了解了如何在年輕一代中保留新物件,如何讓大物件進入老一代,如何將物件的年齡設定為老一代,穩定 j**a 堆與湍流 j**a 堆,增加吞吐量以提高系統效能,嘗試使用大記憶體分頁, 使用非占有垃圾**等主題,通過示例和相應的輸出說明,讓讀者對JVM優化有乙個初步的了解。與其他文章一樣,沒有固定的優化,讀者需要自己判斷和練習才能找到正確的路徑。

相關問題答案

    股票交易的寶貴經驗總結

    一 引言。作為一名擁有數十年經驗的投資者,我深知風險與機遇並存。在這個不確定的市場中,我經歷了無數的起起落落和曲折,也積累了寶貴的經驗和教訓。今天,我想和大家分享這些寶貴的經驗,希望能幫助大家在 中取得更好的成績。第二,賺錢和賠錢的哲學。在 中,快錢往往意味著更高的風險。我周圍的一些朋友,他們試圖通...

    從研究生入學考試的失敗中吸取了哪些教訓?

    親愛的朋友們,大家好!我是秦老師,今天我想和大家聊聊 考研不及格的經歷有哪些總結?這個話題。首先,我想說的是,失敗並不是一件可怕的事情。每個人都會經歷失敗,無論是在學校 工作還是生活中。但關鍵是我們如何從失敗中吸取教訓,從失敗中吸取教訓,然後勇敢地重新開始。因此,今天我想和大家分享一些從研究生入學考...

    水泥廠裝置巡檢經驗總結

    來到水泥熟料燒成車間,從事水泥廠裝置檢驗六年多,一路上檢驗了很多崗位,經歷了很多裝置事故,這些造就了我的檢驗知識,為我積累了很多寶貴的經驗,我很高興站在這裡與大家分享檢驗經驗。在座的各位領導 各位同事,不妨想想一下,你心目中的檢查員是什麼樣子的?這不是簡單的加油,寫記錄,而不僅僅是知道如何報告裝置是...

    資料回顧與經驗總結 2024年獨立站黑五網銷售資料報告

    據美國公司Odobie Analytics稱,今年在美國 黑色星期五 月 日 支出創紀錄億美元,同比增長 網路星期一 月 日 銷售額約為 億美元 黑色星期五 和 網路星期一 之間的週末。銷售額增長 至億美元 在當前經濟形勢下,美國消費者對今年購物季的活動仍印象深刻,這主要是由於零售商對市場環境和消費...

    個人經歷總結:在日本留學如何花更少的錢

    不知道同學們最擔心留學的是什麼,反正學長們當時最擔心的是什麼。花太多錢怎麼辦?學長不是豪門,久而久之,他總結出一套省錢的玩法。最近有同學問,學長就根據自己的經驗告訴同學們。.去日本前的準備 首先是中介費,它不會向中介扔錢。有腦子可以試試DIY,想省事省力,可以找個免費的中介申請。如今,網際網絡上有很...