JAVA進程啟動的時候材义,雖然我們可以為JVM指定合適的內(nèi)存大小乞封,但是這些內(nèi)存操作系統(tǒng)并沒有真正的分配給JVM,而是等JVM訪問這些內(nèi)存的時候,才真正分配,這樣會造成以下問題:
- 第1次YGC之前Eden區(qū)分配對象的速度較慢;
- YGC的時候邻辉,Young區(qū)的對象要晉升到Old區(qū)的時候猿棉,這個時候需要操作系統(tǒng)真正分配內(nèi)存节吮,這樣就會加大YGC的停頓時間;
啟動時間
配置-XX:+AlwaysPreTouch參數(shù)可以優(yōu)化這個問題,不過這個參數(shù)也有副作用瘪匿,它會影響啟動時間跛梗,那影響到底有多大呢?請接著往下看柿顶。
對比結(jié)果如下茄袖,差距還是蠻大的:
~ | -XX:+AlwaysPreTouch | XX:-AlwaysPreTouch(default) |
---|---|---|
16G | 36s | <1s |
8G | 20s | <1s |
并行PreTouch
配置這個參數(shù)后這么耗時其中一個原因是操软,這個特性在JDK8版本以前都不是并行處理的嘁锯,到了JDK9才是并行。可以戳鏈接Parallelize Memory Pretouch: https://bugs.openjdk.java.net/browse/JDK-8157952
根本原因
配置-XX:+AlwaysPreTouch參數(shù)后家乘,JVM進程啟動時間慢了幾個數(shù)量級的根本原因呢蝗羊?
在沒有配置-XX:+AlwaysPreTouch參數(shù)即默認情況下,JVM參數(shù)-Xms申明的堆只是在虛擬內(nèi)存中分配仁锯,而不是在物理內(nèi)存中分配:它被以一種內(nèi)部數(shù)據(jù)結(jié)構(gòu)的形式記錄耀找,從而避免被其他進程使用這些內(nèi)存。這些內(nèi)存頁直到被訪問時业崖,才會在物理內(nèi)存中分配野芒。當JVM需要內(nèi)存的時候,操作系統(tǒng)將根據(jù)需要分配內(nèi)存頁双炕。
配置-XX:+AlwaysPreTouch參數(shù)后狞悲,JVM將-Xms指定的堆內(nèi)存中每個字節(jié)都寫入'0',這樣的話妇斤,除了在虛擬內(nèi)存中以內(nèi)部數(shù)據(jù)結(jié)構(gòu)保留之外摇锋,還會在物理內(nèi)存中分配。并且由于touch這個行為是單線程的站超,因此它將會讓JVM進程啟動變慢荸恕。所以,要么選擇減少接下來對每個緩存頁的第一次訪問時間死相,要么選擇減少JVM進程啟動時間融求,這是一種trade-off。
對G1無效
G1前提下媳纬,即使配置了-XX:+AlwaysPreTouch參數(shù)双肤,JVM也會忽略掉這個參數(shù),即跟沒有配置效果一樣钮惠。8u60版本修復了這個問題茅糜,詳情請戳鏈接:G1 ignores AlwaysPreTouch: https://bugs.openjdk.java.net/browse/JDK-8067469,如下圖所示:
轉(zhuǎn)自 阿飛的博客