摘要:如果開(kāi)啟,則每次后會(huì)重新計(jì)算和區(qū)的大小,計(jì)算依據(jù)是過(guò)程中統(tǒng)計(jì)的時(shí)間吞吐量?jī)?nèi)存占用量。應(yīng)用達(dá)到預(yù)期的吞吐量,即應(yīng)用正常運(yùn)行時(shí)間正常運(yùn)行時(shí)間耗時(shí)。理論上,增大內(nèi)存,可以降低的頻率,以此達(dá)到預(yù)期吞吐量。
轉(zhuǎn)載請(qǐng)注明原文鏈接:https://www.jianshu.com/p/741...一、AdaptiveSizePolicy簡(jiǎn)介
AdaptiveSizePolicy(自適應(yīng)大小策略) 是 JVM GC Ergonomics(自適應(yīng)調(diào)節(jié)策略) 的一部分。
如果開(kāi)啟 AdaptiveSizePolicy,則每次 GC 后會(huì)重新計(jì)算 Eden、From 和 To 區(qū)的大小,計(jì)算依據(jù)是 GC 過(guò)程中統(tǒng)計(jì)的 GC 時(shí)間、吞吐量、內(nèi)存占用量。
開(kāi)啟 AdaptiveSizePolicy 的參數(shù)為:
-XX:+UseAdaptiveSizePolicy
JDK 1.8 默認(rèn)使用 UseParallelGC 垃圾回收器,該垃圾回收器默認(rèn)啟動(dòng)了 AdaptiveSizePolicy。
AdaptiveSizePolicy 有三個(gè)目標(biāo):
Pause goal:應(yīng)用達(dá)到預(yù)期的 GC 暫停時(shí)間。
Throughput goal:應(yīng)用達(dá)到預(yù)期的吞吐量,即應(yīng)用正常運(yùn)行時(shí)間 / (正常運(yùn)行時(shí)間 + GC 耗時(shí))。
Minimum footprint:盡可能小的內(nèi)存占用量。
AdaptiveSizePolicy 為了達(dá)到三個(gè)預(yù)期目標(biāo),涉及以下操作:
如果 GC 停頓時(shí)間超過(guò)了預(yù)期值,會(huì)減小內(nèi)存大小。理論上,減小內(nèi)存,可以減少垃圾標(biāo)記等操作的耗時(shí),以此達(dá)到預(yù)期停頓時(shí)間。
如果應(yīng)用吞吐量小于預(yù)期,會(huì)增加內(nèi)存大小。理論上,增大內(nèi)存,可以降低 GC 的頻率,以此達(dá)到預(yù)期吞吐量。
如果應(yīng)用達(dá)到了前兩個(gè)目標(biāo),則嘗試減小內(nèi)存,以減少內(nèi)存消耗。
注:AdaptiveSizePolicy 涉及的內(nèi)容比較廣,本文主要關(guān)注 AdaptiveSizePolicy 對(duì)年輕代大小的影響,以及隨之產(chǎn)生的問(wèn)題。
AdaptiveSizePolicy 看上去很智能,但有時(shí)它也很調(diào)皮,會(huì)引發(fā) GC 問(wèn)題。
二、由 AdaptiveSizePolicy 引發(fā)的 GC 問(wèn)題某一天,有一位群友在群里發(fā)來(lái)一張 jmap -heap 內(nèi)存使用情況圖。
說(shuō) Survivor 區(qū)占比總是在 98% 以上。
仔細(xì)觀察這張圖,其中包含幾個(gè)重要信息:
From 和 To 區(qū)都比較小,只有 10M。容量比較小,才顯得占比高。
Old 區(qū)的占比和使用量(兩個(gè)多 G)都比較高。
此外,還可以看到 Eden、From、To 之間的比例不是默認(rèn)的 8:1:1。
于是,立馬就想到 AdaptiveSizePolicy。
經(jīng)群友的確認(rèn),使用的是 JDK 1.8 的默認(rèn)回收算法。
JVM 參數(shù)配置如下:
參數(shù)中沒(méi)有對(duì) GC 算法進(jìn)行配置,即使用默認(rèn)的 UseParallelGC。
用默認(rèn)參數(shù)啟動(dòng)一個(gè)基于 JDK 1.8 的應(yīng)用,然后使用 jinfo -flags pid 即可查看默認(rèn)配置的 GC 算法。
上文提到,該算法默認(rèn)開(kāi)啟 AdaptiveSizePolicy。
即使 SurvivorRatio 的默認(rèn)值是 8,但年輕代三個(gè)區(qū)域之間的比例仍會(huì)變動(dòng)。
這個(gè)問(wèn)題,可以參考來(lái)自R大的回答:
http://hllvm.group.iteye.com/...
HotSpot VM里,ParallelScavenge系的GC(UseParallelGC / UseParallelOldGC)默認(rèn)行為是SurvivorRatio如果不顯式設(shè)置就沒(méi)啥用。顯式設(shè)置到跟默認(rèn)值一樣的值則會(huì)有效果。因?yàn)镻arallelScavenge系的GC最初設(shè)計(jì)就是默認(rèn)打開(kāi)AdaptiveSizePolicy的,它會(huì)自動(dòng)、自適應(yīng)的調(diào)整各種參數(shù)。
在群友的截圖中,F(xiàn)rom 區(qū)只有 10M,Eden 區(qū)占用了卻超過(guò)年輕代八成的空間。
其原因是 AdaptiveSizePolicy 為了達(dá)到期望的目標(biāo)而進(jìn)行了調(diào)整。
大概定位了 Survivor 區(qū)小的原因,還有一個(gè)問(wèn)題:
為什么老年代的占比和使用量都比較高?
于是群友使用 jmap -histo 查看堆中的實(shí)例。
可以看出,其中有兩個(gè)類(lèi)的實(shí)例比較多,分別是:
LinkedHashMap$Entry
ExpiringCache$Entry
于是,搜索關(guān)鍵類(lèi) ExpiringCache。
可以看出在 ExpiringCache 的構(gòu)造函數(shù)中,初始化了一個(gè) LinkedHashMap。
懷疑 LinkedHashMap$Entry 數(shù)量多的原因和 ExpiringCache$Entry 直接有關(guān)。
ExpiringCache(long millisUntilExpiration) { this.millisUntilExpiration = millisUntilExpiration; map = new LinkedHashMap() { protected boolean removeEldestEntry(Map.Entry eldest) { return size() > MAX_ENTRIES; } }; }
注:該 map 用于保存緩存數(shù)據(jù),設(shè)置了淘汰機(jī)制。當(dāng) map 大小超過(guò) MAX_ENTRIES = 200 時(shí),會(huì)開(kāi)始淘汰。
接著查看 ExpiringCache$Entry 類(lèi)。
這個(gè)類(lèi)的主要屬性是「時(shí)間戳」和「值」,時(shí)間戳用于超時(shí)淘汰(緩存常用手法)。
static class Entry { private long timestamp; private String val; …… }
接著查看哪里使用到了這個(gè)緩存。
于是找到 get 方法,定位到只有一個(gè)類(lèi)的一個(gè)方法使用到了這個(gè)緩存。
接著往上層找,看到了一個(gè)熟悉的類(lèi):File,它的 getCanonicalPath() 方法使用到了這個(gè)緩存。
該方法用于獲取文件路徑。
于是,詢(xún)問(wèn)群友,是否在項(xiàng)目中使用了 getCanonicalPath() 方法。
得到的回答是肯定的。
當(dāng)項(xiàng)目中使用 getCanonicalPath() 方法獲取文件路徑時(shí),會(huì)發(fā)生以下的事情:
首先從緩存中讀取,取不到則需要生成緩存。
生成緩存需要新建 ExpiringCache$Entry 對(duì)象用于保存緩存值,這些新建的對(duì)象都會(huì)被分配到 Eden 區(qū)。
當(dāng)大量使用 getCanonicalPath() 方法時(shí),緩存數(shù)量超過(guò) MAX_ENTRIES = 200 開(kāi)啟淘汰策略。原來(lái) map 中的 ExpiringCache$Entry 對(duì)象變成垃圾對(duì)象,真正存活的 Entry 只有 200 個(gè)。
當(dāng)發(fā)生 YGC 時(shí),理論上存活的 200 個(gè) Entry 會(huì)去往 To 區(qū),其他被淘汰的垃圾 Entry 對(duì)象會(huì)被回收。
但由于 AdaptiveSizePolicy 將 To 區(qū)調(diào)整到只有 10MB,裝不下本該移動(dòng)到 To 區(qū)的對(duì)象,只能直接移動(dòng)到老年代。
于是,在每次 YGC 時(shí),會(huì)有接近 200 個(gè)存活的 ExpiringCache$Entry 對(duì)象進(jìn)入到老年代。隨著緩存淘汰機(jī)制的運(yùn)行,這些 Entry 對(duì)象立馬又變成垃圾。
當(dāng)對(duì)象進(jìn)入老年代,即使變成了垃圾,也需要等到老年代 GC 或者 FGC 才能將其回收。由于老年代容量較大,可以承受多次 YGC 給予的 200 個(gè) ExpiringCache$Entry 對(duì)象。
于是,老年代使用量逐漸變高。
老年代內(nèi)存占用量高的問(wèn)題也定位到了。
因?yàn)槊看?YGC 只有 200 個(gè)實(shí)例進(jìn)入到老年代,問(wèn)題顯得比較溫和。
只是隔一段時(shí)間觸發(fā) FGC,應(yīng)用運(yùn)行看似正常。
接著使用 jstat -gcutil 查看 GC 情況。
可以看到從應(yīng)用啟動(dòng),一共發(fā)生了 15654 次 YGC。
推算每次 YGC 有 200 個(gè) ExpiringCache$Entry 對(duì)象進(jìn)入老年代。
那么,老年代中大約存在 3130800 個(gè) ExpiringCache$Entry 對(duì)象。
從之前的 jmap -histo 結(jié)果中看到,ExpiringCache$Entry 對(duì)象的數(shù)量是 6118824 個(gè)。
兩個(gè)數(shù)目都為百萬(wàn)級(jí)。其余約 300W 個(gè)實(shí)例應(yīng)該都在 Eden 區(qū)。
每一次 YGC 后,都會(huì)有大量的 ExpiringCache$Entry 對(duì)象被回收。
從群友截取的 GC log 中可以看出,YGC 的頻率大概為 23 秒一次。
假設(shè)運(yùn)行的 jmap -histo 命令是在即將觸發(fā) YGC 之前。
那么,應(yīng)用大概在 20s 的事件內(nèi)產(chǎn)生了 300W 個(gè) ExpiringCache$Entry 實(shí)例,1s 內(nèi)產(chǎn)生約 15W 個(gè)。
假設(shè)單機(jī) QPS = 300,一次請(qǐng)求產(chǎn)生的 ExpiringCache$Entry 實(shí)例數(shù)約為 500 個(gè)。
猜測(cè)是在循環(huán)體中使用了 getCanonicalPath() 方法。
至此可以得出 Survior 區(qū)變小,老年代占比變高的原因:
在默認(rèn) SurvivorRatio = 8 的情況下,沒(méi)有達(dá)到吞吐量的期望,AdaptiveSizePolicy 加大了 Eden 區(qū)的大小。From 和To 區(qū)被壓縮到只有 10M。
在項(xiàng)目中大量使用 getCanonicalPath() 方法,產(chǎn)生大量ExpiringCache$Entry 實(shí)例。
當(dāng) YGC 發(fā)生時(shí)候,由于 To 區(qū)太小,存活的 Entry 對(duì)象直接進(jìn)入到老年代。老年代占用量逐漸變大。
從群友的 jstat -gcutil 截圖中還可以看出,應(yīng)用從啟動(dòng)到使用該命令,觸發(fā)了 19 次 FGC,一共耗時(shí) 9.933s,平均每次 FGC 耗時(shí)為 520ms。
這樣的停頓時(shí)間,對(duì)于一個(gè)高 QPS 的應(yīng)用是無(wú)法忍受的。
定位到了問(wèn)題的原因,解決方案比較簡(jiǎn)單。
解決的思路有兩個(gè):
不使用緩存,就不會(huì)生成大量 ExpiringCache$Entry 實(shí)例。
阻止 AdaptiveSizePolicy 縮小 To 區(qū)。讓 YGC 時(shí)存活的 ExpiringCache$Entry 對(duì)象都能順利進(jìn)入 To 區(qū),保留在年輕代,而不是進(jìn)入老年代。
解決方案一:
不使用緩存。
使用 -Dsun.io.useCanonCaches = false 參數(shù)即可關(guān)閉緩存。
這種方案解決比較方便,但這個(gè)參數(shù)并非常規(guī)參數(shù),慎用。
解決方案二:
保持使用 UseParallelGC,顯式設(shè)置 -XX:SurvivorRatio=8。
配置參數(shù)進(jìn)行測(cè)試:
看到默認(rèn)配置下,三者之間的比例不是 8:1:1。
可以看到,加上參數(shù) -Xmn100m -XX:SurvivorRatio=8 參數(shù)后,固定了 Eden 和 Survivor 之間的比例。
解決方案三:
使用 CMS 垃圾回收器。
CMS 默認(rèn)關(guān)閉 AdaptiveSizePolicy。
配置參數(shù) -XX:+UseConcMarkSweepGC,通過(guò) jinfo 命令查看,可以看到 CMS 默認(rèn)減去/不使用 AdaptiveSizePolicy。
群友也是采用了這個(gè)方法:
可以看出,Eden 和 Survivor 之間的比例被固定,To 區(qū)沒(méi)有被縮小。老年代的使用量和使用率也都很正常。
三、源碼層面了解 AdaptiveSizePolicy注:以下源碼均主要基于 openjdk 8,不同 jdk 版本之間會(huì)有區(qū)別。
對(duì)源碼的理解程度有限,對(duì)源碼的理解也一直在路上。
有任何錯(cuò)誤,還請(qǐng)各位指正,謝謝。
首先解釋?zhuān)瑸槭裁丛?UseParallelGC 回收器的前提下,顯式配置 SurvivorRatio 即可固定年輕代三個(gè)區(qū)域之間的比例。
在 arguments.cpp 類(lèi)中有一個(gè) set_parallel_gc_flags() 方法。
從方法命名來(lái)看,是為了設(shè)置并行回收器的參數(shù)。
// If InitialSurvivorRatio or MinSurvivorRatio were not specified, but the // SurvivorRatio has been set, reset their default values to SurvivorRatio + // 2. By doing this we make SurvivorRatio also work for Parallel Scavenger. // See CR 6362902 for details. if (!FLAG_IS_DEFAULT(SurvivorRatio)) { if (FLAG_IS_DEFAULT(InitialSurvivorRatio)) { FLAG_SET_DEFAULT(InitialSurvivorRatio, SurvivorRatio + 2); } if (FLAG_IS_DEFAULT(MinSurvivorRatio)) { FLAG_SET_DEFAULT(MinSurvivorRatio, SurvivorRatio + 2); } }
當(dāng)顯式設(shè)置 SurvivorRatio,即 !FLAG_IS_DEFAULT(SurvivorRatio),該方法會(huì)設(shè)置別的參數(shù)。
方法注釋上寫(xiě)著:
make SurvivorRatio also work for Parallel Scavenger
通過(guò)顯式設(shè)置 SurvivorRatio 參數(shù),SurvivorRatio 就會(huì)在 Parallel Scavenge 回收器中生效。
至于為何會(huì)生效,還有待進(jìn)一步學(xué)習(xí)。
而默認(rèn)是會(huì)被 AdaptiveSizePolicy 調(diào)整的。
接著查看 AdaptiveSizePolicy 動(dòng)態(tài)調(diào)整內(nèi)存大小的代碼。
JDK 1.8 默認(rèn)的 UseParallelGC 回收器,其對(duì)應(yīng)的年輕代回收算法是 Parallel Scavenge。
觸發(fā) GC 的原因有多種,最普通的一種是在年輕代分配內(nèi)存失敗。
UseParallelGC 分配內(nèi)存失敗引發(fā) GC 的入口位于
vmPSOperations.cpp 類(lèi)的 VM_ParallelGCFailedAllocation::doit() 方法。
之后依次調(diào)用了以下方法:
parallelScavengeHeap.cpp 類(lèi)的 failed_mem_allocate(size_t size) 方法。
psScavenge.cpp 類(lèi)的 invoke()、invoke_no_policy() 方法。
invoke_no_policy() 方法中有一段代碼涉及 AdaptiveSizePolicy。
if (UseAdaptiveSizePolicy) { …… size_policy->compute_eden_space_size(young_live, eden_live, cur_eden, max_eden_size, false /* not full gc*/); …… }
在 GC 主過(guò)程完成后,如果開(kāi)啟 UseAdaptiveSizePolicy 則會(huì)重新計(jì)算 Eden 區(qū)的大小。
在 compute_eden_space_size 方法中,有幾個(gè)判斷。
對(duì)應(yīng) AdaptiveSizePolicy 的三個(gè)目標(biāo):
與預(yù)期 GC 停頓時(shí)間對(duì)比。
與預(yù)期吞吐量對(duì)比。
如果達(dá)到預(yù)期,則調(diào)整內(nèi)存容量。
if ((_avg_minor_pause->padded_average() > gc_pause_goal_sec()) || (_avg_major_pause->padded_average() > gc_pause_goal_sec())) { adjust_eden_for_pause_time(is_full_gc, &desired_promo_size, &desired_eden_size); } else if (_avg_minor_pause->padded_average() > gc_minor_pause_goal_sec()) { adjust_eden_for_minor_pause_time(is_full_gc, &desired_eden_size); } else if(adjusted_mutator_cost() < _throughput_goal) { assert(major_cost >= 0.0, "major cost is < 0.0"); assert(minor_cost >= 0.0, "minor cost is < 0.0"); adjust_eden_for_throughput(is_full_gc, &desired_eden_size); } else { if (UseAdaptiveSizePolicyFootprintGoal && young_gen_policy_is_ready() && avg_major_gc_cost()->average() >= 0.0 && avg_minor_gc_cost()->average() >= 0.0) { size_t desired_sum = desired_eden_size + desired_promo_size; desired_eden_size = adjust_eden_for_footprint(desired_eden_size, desired_sum); } }
詳細(xì)看其中一個(gè)判斷。
if ((_avg_minor_pause->padded_average() > gc_pause_goal_sec()) || (_avg_major_pause->padded_average() > gc_pause_goal_sec()))
如果統(tǒng)計(jì)的 YGC 或者 Old GC 時(shí)間超過(guò)了目標(biāo)停頓時(shí)間,則會(huì)調(diào)用 adjust_eden_for_pause_time 調(diào)整 Eden 區(qū)大小。
gc_pause_goal_sec() 方法獲取預(yù)期停頓時(shí)間,在 ParallelScavengeHeap::initialize() 方法中,通過(guò)讀取 JVM 參數(shù) MaxGCPauseMillis 獲取。
接下來(lái),再看 CMS 回收器。
CMS 初始化分代位于 cmsCollectorPolicy.cpp 類(lèi)的 initialize_generations() 方法。
if (UseParNewGC) { if (UseAdaptiveSizePolicy) { _generations[0] = new GenerationSpec(Generation::ASParNew, _initial_gen0_size, _max_gen0_size); } else { _generations[0] = new GenerationSpec(Generation::ParNew, _initial_gen0_size, _max_gen0_size); } } else { _generations[0] = new GenerationSpec(Generation::DefNew, _initial_gen0_size, _max_gen0_size); } if (UseAdaptiveSizePolicy) { _generations[1] = new GenerationSpec(Generation::ASConcurrentMarkSweep, _initial_gen1_size, _max_gen1_size); } else { _generations[1] = new GenerationSpec(Generation::ConcurrentMarkSweep, _initial_gen1_size, _max_gen1_size); }
其中 _generations[0] 代表年輕代特征,_generations[1] 代表老年代特征。
如果設(shè)置不同的 UseParNewGC 、UseAdaptiveSizePolicy 參數(shù),會(huì)對(duì)年輕代和老年代使用不同的策略。
CMS 垃圾回收入口位于 genCollectedHeap.cpp 類(lèi)的 do_collection 方法。
在 do_collection 方法中,GC 主過(guò)程完成后,會(huì)對(duì)每個(gè)分代進(jìn)行大小調(diào)整。
for (int j = max_level_collected; j >= 0; j -= 1) { // Adjust generation sizes. _gens[j]->compute_new_size(); }
本文主要討論 AdaptiveSizePolicy 對(duì)年輕代的影響,主要看 ASParNewGeneration 類(lèi),其中的 AS 前綴就是 AdaptiveSizePolicy 的意思。
如果設(shè)置 -XX:+UseAdaptiveSizePolicy 則年輕代對(duì)應(yīng) ASParNewGeneration 類(lèi),否則對(duì)應(yīng) ParNewGeneration 類(lèi)。
在 ASParNewGeneration 類(lèi)中 compute_new_size() 方法中,調(diào)用了另一個(gè)方法調(diào)整 Eden 區(qū)大小。
size_policy->compute_eden_space_size(eden()->capacity(), max_gen_size());
該方法與 Parallel Scavenge 的 compute_eden_space_size 方法類(lèi)似,也從三個(gè)方面對(duì)內(nèi)存大小進(jìn)行調(diào)整,分別是:
adjust_eden_for_pause_time
adjust_eden_for_throughput
adjust_eden_for_footprint
接著進(jìn)行測(cè)試,設(shè)置參數(shù) -XX:+UseAdaptiveSizePolicy、
-XX:+UseConcMarkSweepGC。
期望 CMS 會(huì)啟用 AdaptiveSizePolicy,但根據(jù) jmap -heap 結(jié)果查看,并沒(méi)有啟動(dòng),年輕代三個(gè)區(qū)域之間的比例為 8:1:1。
從 jinfo 命令結(jié)果也可以看出,即使設(shè)置了 -XX:+UseAdaptiveSizePolicy,仍然關(guān)閉了 AdaptiveSizePolicy。
因?yàn)樵?JDK 1.8 中,如果使用 CMS,無(wú)論 UseAdaptiveSizePolicy 如何設(shè)置,都會(huì)將 UseAdaptiveSizePolicy 設(shè)置為 false。
查看 arguments.cpp 類(lèi)中的 set_cms_and_parnew_gc_flags 方法,其調(diào)用了 disable_adaptive_size_policy 方法將 UseAdaptiveSizePolicy 設(shè)置成 false。
static void disable_adaptive_size_policy(const char* collector_name) { if (UseAdaptiveSizePolicy) { if (FLAG_IS_CMDLINE(UseAdaptiveSizePolicy)) { warning("disabling UseAdaptiveSizePolicy; it is incompatible with %s.", collector_name); } FLAG_SET_DEFAULT(UseAdaptiveSizePolicy, false); } }
如果是在啟動(dòng)參數(shù)中設(shè)置了,則會(huì)打出提醒。
但在 JDK 1.6 和 1.7 中,set_cms_and_parnew_gc_flags 方法的邏輯和 1.8 中的不同。
如果 UseAdaptiveSizePolicy 參數(shù)是默認(rèn)的,則強(qiáng)制設(shè)置成 false。
如果顯式設(shè)置(complete),則不做改變。
// Turn off AdaptiveSizePolicy by default for cms until it is // complete. if (FLAG_IS_DEFAULT(UseAdaptiveSizePolicy)) { FLAG_SET_DEFAULT(UseAdaptiveSizePolicy, false); }
于是嘗試使用 JDK 1.6 搭建 web 應(yīng)用,加上 -XX:+UseAdaptiveSizePolicy、-XX:+UseConcMarkSweepGC 兩個(gè)參數(shù)。
再用 jinfo -flag 查看,看到兩個(gè)參數(shù)都被置為 true。
接著,使用 jmap -heap 查看堆內(nèi)存使用情況,發(fā)現(xiàn)展示不了信息。
這其實(shí)是 JDK 低版本的一個(gè) Bug。
1.6.30以上到1.7的全部版本已經(jīng)確認(rèn)有該問(wèn)題,jdk8修復(fù)。
參考:UseAdaptiveSizePolicy與CMS垃圾回收同時(shí)使用導(dǎo)致的JVM報(bào)錯(cuò) https://www.cnblogs.com/moona...
四、問(wèn)題小結(jié)現(xiàn)階段大多數(shù)應(yīng)用使用 JDK 1.8,其默認(rèn)回收器是 Parallel Scavenge,并且默認(rèn)開(kāi)啟了 AdaptiveSizePolicy。
AdaptiveSizePolicy 動(dòng)態(tài)調(diào)整 Eden、Survivor 區(qū)的大小,存在將 Survivor 區(qū)調(diào)小的可能。當(dāng) Survivor 區(qū)被調(diào)小后,部分 YGC 后存活的對(duì)象直接進(jìn)入老年代。老年代占用量逐漸上升從而觸發(fā) FGC,導(dǎo)致較長(zhǎng)時(shí)間的 STW。
建議使用 CMS 垃圾回收器,默認(rèn)關(guān)閉 AdaptiveSizePolicy。
建議在 JVM 參數(shù)中加上 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution,讓 GC log 更加詳細(xì),方便定位問(wèn)題。
五、參考資料Garbage Collector Ergonomics
File,file.getPath(), getAbsolutePath(), getCanonicalPath()區(qū)別
UseAdaptiveSizePolicy與CMS垃圾回收同時(shí)使用導(dǎo)致的JVM報(bào)錯(cuò)
JVM分析工具概述
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/77153.html
摘要:本文將介紹的參數(shù)的重要性以及在發(fā)生時(shí)對(duì)系統(tǒng)整體性能的顯著影響。我們來(lái)看下的選項(xiàng)在發(fā)生時(shí)會(huì)對(duì)系統(tǒng)帶來(lái)哪些影響。所以這些請(qǐng)求將會(huì)放到堆積隊(duì)列,隊(duì)列的長(zhǎng)度是的中設(shè)置的。從而導(dǎo)致進(jìn)程的數(shù)量超過(guò),并觸發(fā)了操作系統(tǒng)進(jìn)行內(nèi)存交換的閥值。 原文鏈接:http://www.cubrid.org/blog/dev-platform/maxclients-in-apache-and-its-effect-o...
摘要:前言最近在看實(shí)戰(zhàn)虛擬機(jī)發(fā)現(xiàn)書(shū)上的一個(gè)關(guān)于局部變量表挺有意思,先上代碼。主角沒(méi)有分配了一塊的堆空間,并使用局部變量引用這塊空間然后顯式進(jìn)行一次。 前言 最近在看《實(shí)戰(zhàn)Java虛擬機(jī)》, 發(fā)現(xiàn)書(shū)上的一個(gè)關(guān)于局部變量表GC挺有意思,先上代碼。 主角 沒(méi)有GC public class Main { public static void reversion(){ { ...
學(xué)習(xí)JVM的相關(guān)資料 《深入理解Java虛擬機(jī)——JVM高級(jí)特性與最佳實(shí)踐(第2版)》 showImg(https://segmentfault.com/img/bVbsqF5?w=200&h=200); 基于最新JDK1.7,圍繞內(nèi)存管理、執(zhí)行子系統(tǒng)、程序編譯與優(yōu)化、高效并發(fā)等核心主題對(duì)JVM進(jìn)行全面而深入的分析,深刻揭示JVM的工作原理。以實(shí)踐為導(dǎo)向,通過(guò)大量與實(shí)際生產(chǎn)環(huán)境相結(jié)合的案例展示了解...
摘要:監(jiān)控告警是運(yùn)營(yíng)系統(tǒng)最核心的功能之一,騰訊內(nèi)部有一套很成熟的監(jiān)控告警平臺(tái),而且開(kāi)發(fā)運(yùn)維同學(xué)已經(jīng)習(xí)慣這套平臺(tái),如果我們針對(duì)容器再開(kāi)發(fā)一個(gè)監(jiān)控告警平臺(tái),會(huì)花費(fèi)很多精力,而且沒(méi)有太大的意義。也是一款付費(fèi)監(jiān)控解決方案,計(jì)劃收費(fèi)方案是美分小時(shí)。 如今,越來(lái)越多的公司開(kāi)始使用 Docker 了,現(xiàn)在來(lái)給大家看幾組數(shù)據(jù): 2 / 3 的公司在嘗試了 Docker 后最終使用了它 也就是說(shuō) Docker...
摘要:第二步分析第一個(gè)循環(huán)即表示產(chǎn)生的對(duì)象,后面規(guī)律相同和會(huì)直接放入?yún)^(qū)。因?yàn)閮?yōu)先放區(qū),而且夠放,此時(shí)為兩者表示已用剩余。此值一般設(shè)置與相同以避免每次垃圾回收完后重新分配內(nèi)存設(shè)置年輕代大小為。 一、概述 閑來(lái)有空翻翻書(shū),撿撿一些基礎(chǔ)點(diǎn),就當(dāng)靜下心多寫(xiě)字。Java基礎(chǔ)的東西無(wú)論怎么樣都會(huì)想到JVM,而提JVM必然想到最常見(jiàn)的一些點(diǎn):字節(jié)碼加載,類(lèi)初始化,方法執(zhí)行,對(duì)象內(nèi)存分配和回收,線程和鎖機(jī)制...
閱讀 1895·2021-11-17 09:33
閱讀 6489·2021-10-12 10:20
閱讀 2310·2021-09-22 15:50
閱讀 1798·2021-09-22 15:10
閱讀 631·2021-09-10 10:51
閱讀 636·2021-09-10 10:50
閱讀 3059·2021-08-11 11:19
閱讀 1788·2019-08-30 15:55