成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

由「Metaspace容量不足觸發(fā)CMS GC」從而引發(fā)的思考

StonePanda / 1721人閱讀

摘要:第一個大陡坡是應(yīng)用發(fā)布,老年代內(nèi)存占比下降,很正常。但此時老年代內(nèi)存使用占比。因為后期并不會引發(fā)??梢钥闯觯捎诘竭_時候,觸發(fā)了一次和一次。但觸發(fā)時,占比并沒用明顯的規(guī)律。得出,擴容導致這個說法,其實是不準確的。

轉(zhuǎn)載請注明原文鏈接:https://www.jianshu.com/p/468...

某天早上,毛老師在群里問「cat 上怎么看 gc」。

看到有 GC 的問題,立馬做出小雞搓手狀。

之后毛老師發(fā)來一張圖。

圖片展示了老年代內(nèi)存占用情況。

第一個大陡坡是應(yīng)用發(fā)布,老年代內(nèi)存占比下降,很正常。

第二個小陡坡,老年代內(nèi)存占用突然下降,應(yīng)該是發(fā)生了老年代 GC。

但奇怪的是,此時老年代內(nèi)存占用并不高,發(fā)生 GC 并不是正常現(xiàn)象。

于是,毛老師查看了 GC log。

從 GC log 中可以看出,老年代發(fā)生了一次 CMS GC。

但此時老年代內(nèi)存使用占比 = 234011K / 2621440k ≈ 9%。

而 CMS 觸發(fā)的條件是:

老年代內(nèi)存使用占比達到 CMSInitiatingOccupancyFraction,默認為 92%,

毛老師設(shè)置的是 75%。

-XX:CMSInitiatingOccupancyFraction = 75

于是排除老年代占用過高的可能。

接著分析內(nèi)存狀況。

毛老師發(fā)現(xiàn)在老年代發(fā)生 GC 時,Metaspace 的內(nèi)存占用也一起下降。

于是懷疑是 Metaspace 占用達到了設(shè)置的參數(shù) MetaspaceSize,發(fā)生了 GC。

查看 JVM 參數(shù)設(shè)置,MetaspaceSize 參數(shù)被設(shè)置為128m。

-XX:MetaspaceSize = 128m -XX:MaxMetaspaceSize = 256m

問題的原因被集中在 Metaspace 上。

毛老師查看另外一個監(jiān)控工具,發(fā)生小陡坡的縱坐標的確接近 128m。

此時,引發(fā)出另一個問題:

Metaspace 發(fā)生 GC,為何會引起老年代 GC。

于是,想到之前看過 阿飛Javaer 的文章 《JVM參數(shù)MetaspaceSize的誤解》。

其中有幾個關(guān)鍵點:

Metaspace 在空間不足時,會進行擴容,并逐漸達到設(shè)置的 MetaspaceSize。

Metaspace 擴容到 -XX:MetaspaceSize 參數(shù)指定的量,就會發(fā)生 FGC。

如果配置了 -XX:MetaspaceSize,那么觸發(fā) FGC 的閾值就是配置的值。

如果 Old 區(qū)配置 CMS 垃圾回收,那么擴容引起的 FGC 也會使用 CMS 算法進行回收。

其中的關(guān)鍵點是:

如果老年代設(shè)置了 CMS,則 Metasapce 擴容引起的 FGC 會轉(zhuǎn)變成一次 CMS。

查看毛老師配置的 JVM 參數(shù),果然設(shè)置了 CMS GC。

-XX:+UseConcMarkSweepGC

于是,解決問題的方法是調(diào)整 -XX:MetaspaceSize = 256m。

從監(jiān)控來看,設(shè)置 -XX:MaxMetaspaceSize = 256m 已經(jīng)足夠。

因為后期并不會引發(fā) CMS GC。

GC 的問題算是解決了,但同時引發(fā)了以下幾點思考:

Metaspace 分配和擴容有什么規(guī)律?

JDK 1.8 中的 Metaspace 和 JDK 1.7 中的 Perm 區(qū)有什么區(qū)別?

老年代回收設(shè)置成非 CMS 時,Metaspace 占用到達 -XX:MetaspaceSize 會引發(fā)什么 GC?

如何制造 Metasapce 內(nèi)存占用上升?

關(guān)于這個問題一和問題二,阿飛Javaer 已經(jīng)解釋的比較清楚。

對于 Metaspce,其初始大小并不等于設(shè)置的 -XX:MetaspaceSize 參數(shù)。

隨著類的加載,Metaspce 會不斷進行擴容,直到達到 -XX:MetaspaceSize 觸發(fā) GC。

而至于如何設(shè)置 Metaspace 的初始大小,目前的確沒有辦法。

在 openjdk 的 bug 列表中,找到一個 關(guān)于 Metaspace 初始大小的 bug,并且尚未解決。

對于問題二, 阿飛Javaer 在文章中也進行了說明。

Perm 的話,我們通過配置 -XX:PermSize 以及 -XX:MaxPermSize 來控制這塊內(nèi)存的大小。

JVM 在啟動的時候會根據(jù) -XX:PermSize 初始化分配一塊連續(xù)的內(nèi)存塊。

這樣的話,如果 -XX:PermSize 設(shè)置過大,就是一種赤果果的浪費。

關(guān)于 Metaspace,JVM 還提供了其余一些設(shè)置參數(shù)。

可以通過以下命令查看。

java -XX:+PrintFlagsFinal -version | grep Metaspace

關(guān)于 Metaspace 更多的內(nèi)容,可以參考笨神的文章:《JVM源碼分析之Metaspace解密》。

問題三

Metaspace 占用到達 -XX:MetaspaceSize 會引發(fā)什么?

已經(jīng)知道,當老年代回收設(shè)置成 CMS GC 時,會觸發(fā)一次 CMS GC。

那么如果不設(shè)置為 CMS GC,又會發(fā)生什么呢?

使用以下配置進行一個小嘗試,然后查看 GC log。

-Xmx2048m -Xms2048m -Xmn1024m 
-XX:MetaspaceSize=40m -XX:MaxMetaspaceSize=128m
-XX:+PrintGCDetails -XX:+PrintGCDateStamps 
-XX:+PrintHeapAtGC -Xloggc:d:/heap_trace.txt

該配置并未設(shè)置 CMS GC,JDK 1.8 默認的老年代回收算法為 ParOldGen。

本文測試的應(yīng)用在啟動完成后,占用 Metaspace 空間約為 63m,可通過 jstat 命令查看。

于是,設(shè)置 -XX:MetaspaceSize = 40m,期望發(fā)生一次 GC。

從 GC log 中,可以找到以下關(guān)鍵日志。

[GC (Metadata GC Threshold) 
[PSYoungGen: 360403K->47455K(917504K)] 360531K->47591K(1966080K), 0.0343563 secs] 
[Times: user=0.08 sys=0.00, real=0.04 secs] 

[Full GC (Metadata GC Threshold) 
[PSYoungGen: 47455K->0K(917504K)] 
[ParOldGen: 136K->46676K(1048576K)] 47591K->46676K(1966080K), 
[Metaspace: 40381K->40381K(1085440K)], 0.1712704 secs] 
[Times: user=0.42 sys=0.02, real=0.17 secs] 

可以看出,由于 Metasapce 到達 -XX:MetaspaceSize = 40m 時候,觸發(fā)了一次 YGC 和一次 Full GC。

一般而言,我們對 Full GC 的重視度比對 YGC 高很多。

所以一般都會直描述,當 Metasapce 到達 -XX:MetaspaceSize 時會觸發(fā)一次 Full GC。

問題四

如何人工模擬 Metaspace 內(nèi)存占用上升?

Metaspace 是 JDK 1.8 之后引入的一個區(qū)域。

有一點可以肯定的,Metaspace 會保存類的描述信息。

JVM 需要根據(jù) Metaspace 中的信息,才能找到堆中類 java.lang.Class 所對應(yīng)的對象。(有點繞)

既然 Metaspace 中會保存類描述信息,可以通過新建類來增加 Metaspace 的占用。

于是想到,使用 CGlib 動態(tài)代理,生成被代理類的子類。

簡單的 SayHello 類。

public class SayHello {
    public void say() {
        System.out.println("hello everyone");
    }
}

簡單的代理類,使用 CGlib 生成子類。

public class CglibProxy implements MethodInterceptor {

    public Object getProxy(Class clazz) {
        Enhancer enhancer = new Enhancer();
        // 設(shè)置需要創(chuàng)建子類的類
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        enhancer.setUseCache(false);
        // 通過字節(jié)碼技術(shù)動態(tài)創(chuàng)建子類實例
        return enhancer.create();
    }

    // 實現(xiàn)MethodInterceptor接口方法
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("前置代理");
        // 通過代理類調(diào)用父類中的方法
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("后置代理");
        return result;
    }
}

簡單新建一個 Controller 用于測試生成 10000 個 SayHello 子類。

@RequestMapping(value = "/getProxy", method = RequestMethod.GET)
@ResponseBody
public void getProxy() {
    CglibProxy proxy = new CglibProxy();
    for (int i = 0; i < 10000; i++) {
        //通過生成子類的方式創(chuàng)建代理類
        SayHello proxyTmp = (SayHello) proxy.getProxy(SayHello.class);
        proxyTmp.say();
    }
}

應(yīng)用啟動完畢后,請求 /getProxy 接口,發(fā)現(xiàn) Metaspace 空間占用上升。

從堆 Dump 中也可以發(fā)現(xiàn),有很多被 CGlib 所代理的 SayHello 類對象。

代理類對應(yīng)的 java.lang.Class 對象分配在堆內(nèi),類的描述信息在 Metaspace 中。

堆中有多個 Class 對象,可以推斷出 Metasapce 需要裝下很多類描述信息。

最后,當 Metaspace 使用空間超過設(shè)置的 -XX:MaxMetaspaceSize=128m 時,就會發(fā)生 OOM。

Exception in thread "http-nio-8080-exec-6" java.lang.OutOfMemoryError: Metaspace

從 GC log 中可以看到,JVM 會在 Metaspace 占用滿之后,嘗試 Full GC。

但會出現(xiàn)以下字樣。

Full GC (Last ditch collection)

此外,還有一個問題。

當 Metaspace 內(nèi)存占用達到 -XX:MetaspaceSize 時,Metaspace 只擴容,不會引起 Full GC。

當 Metaspace 內(nèi)存占用達到 -XX:MetaspaceSize 時,會發(fā)生 Full GC。

在發(fā)生第一次 Full GC 之后,Metaspace 依然會擴容。

那么,第二次觸發(fā) Full GC 的條件是?

有文章說,在觸發(fā)第一次F Full GC 后,之后 Metaspace 的每次擴容,都會引起 Full GC。

但觀察本文測試的 GC log 和 jstat 命令查看 Metasapce 擴容狀況,可以看出:

在第一次 Full GC 之后,之后 Metaspace 的擴容,并不一定會引起 Full GC。

從 jstat 輸出可以看到,在觸發(fā)一次 Full GC 之后,Metaspace 依舊發(fā)生了擴容,但未發(fā)生 Full GC。

jstat FGC 次數(shù)一直都是 1。

此外,使用 GClib 動態(tài)生成類,Metaspace 繼續(xù)擴容,到一定程度,觸發(fā)了 Full GC。

但觸發(fā) FGC 時,Metaspace 占比并沒用明顯的規(guī)律。

嘗試了幾次,由于 jstat 設(shè)置了 1s 鐘輸出一次,所以每次觸發(fā) Full GC 時候,MC 的數(shù)據(jù)都不一樣,但基本是相同。

猜測在第一次 Full GC 之后,之后再次觸發(fā) Full GC 的閾值是有一定的計算公式的。

但具體如何計算,估計是需要深入源碼了。

此外可以看到,每次 Metaspace 擴容時,都伴隨著一次 YGC 或者 Full GC,不知道是否是巧合。

接著看到 占小狼 的文章 《JVM源碼分析之垃圾收集的執(zhí)行過程》。

文章有一句話:

從上述分析中可以發(fā)現(xiàn),gc操作的入口都位于GenCollectedHeap::do_collection方法中。
不同的參數(shù)執(zhí)行不同類型的gc。

打開 openjdk 8 中的 GenCollectedHeap 類,查看 do_collection 方法。

可以看到,在 do_collection 方法中,有這個一段代碼。

if (complete) {
  // Delete metaspaces for unloaded class loaders and clean up loader_data graph
  ClassLoaderDataGraph::purge();
  MetaspaceAux::verify_metrics();
  // Resize the metaspace capacity after full collections
  MetaspaceGC::compute_new_size();
  update_full_collections_completed();
}

其中最主要的是 MetaspaceGC::compute_new_size();。

得出,YGC 和 Full GC 的確會重新計算 Metaspace 的大小。

至于是否進行擴容和縮容,則需要根據(jù) compute_new_size() 方法的計算結(jié)果而定。

得出,Metasapce 擴容導致 GC 這個說法,其實是不準確的。

正確的過程是:新建類導致 Metaspace 容量不夠,觸發(fā) GC,GC 完成后重新計算 Metaspace 新容量,決定是否對 Metaspace 擴容或縮容。

參考資料

JVM參數(shù)MetaspaceSize的誤解 https://www.jianshu.com/p/b44...

JVM源碼分析之垃圾收集的執(zhí)行過程 https://www.jianshu.com/p/04e...

JVM源碼分析之Metaspace解密 http://lovestblog.cn/blog/201...

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/76765.html

相關(guān)文章

  • jvm內(nèi)存分配策略和性能監(jiān)控

    摘要:概述本篇旨在講清楚的內(nèi)存分配策略,日志閱讀,一些常見名詞和提供的一些性能監(jiān)控工具。內(nèi)存分配與回收策略對象優(yōu)先在分配大多數(shù)情況下,對象優(yōu)先在新生代區(qū)中分配。當區(qū)域沒有足夠空間進行分配時,將發(fā)生一次。 概述 本篇旨在講清楚jvm的內(nèi)存分配策略,gc日志閱讀,一些常見名詞和jdk提供的一些性能監(jiān)控工具。廢話不多說,開始上貨。 GC日志閱讀 在開發(fā)的世界里,閱讀日志是最基礎(chǔ)的能力,也是解決問題...

    Baoyuan 評論0 收藏0
  • 學習JVM是如何從入門到放棄?

    摘要:而字節(jié)碼運行在之上,所以不用關(guān)心字節(jié)碼是在哪個操作系統(tǒng)編譯的,只要符合規(guī)范,那么,這個字節(jié)碼文件就是可運行的。好處防止內(nèi)存中出現(xiàn)多份同樣的字節(jié)碼安全性角度特別說明類加載器在成功加載某個類之后,會把得到的類的實例緩存起來。 前言 只有光頭才能變強 JVM在準備面試的時候就有看了,一直沒時間寫筆記?,F(xiàn)在到了一家公司實習,閑的時候就寫寫,刷刷JVM博客,刷刷電子書。 學習JVM的目的也很簡單...

    Joyven 評論0 收藏0
  • 系統(tǒng)優(yōu)化怎么做-Tomcat優(yōu)化

    摘要:運行模式分種模式一般使用模式效率低對系統(tǒng)配置有一些比較高的要求確認的運行模式配置文件關(guān)鍵配置最大線程數(shù)默認是最小活躍線程數(shù)默認是最大的等待隊列個數(shù),超過則請求拒絕默認值是,一般不改變。 前言 Tomcat作為Web應(yīng)用的服務(wù)器,目前絕大多數(shù)公司都是用其作為應(yīng)用服務(wù)器的,應(yīng)用服務(wù)器的執(zhí)行效率會影響系統(tǒng)執(zhí)行,這里會講Tomcat怎樣進行配置能提高處理性能。另外必須提到對應(yīng)的JVM參數(shù)的優(yōu)化...

    gghyoo 評論0 收藏0
  • JVM 完整深入解析

    摘要:堆內(nèi)存的劃分在里面的示意圖垃圾回收一判斷對象是否要回收的方法可達性分析法可達性分析法通過一系列對象作為起點進行搜索,如果在和一個對象之間沒有可達路徑,則稱該對象是不可達的。 工作之余,想總結(jié)一下JVM相關(guān)知識。 Java運行時數(shù)據(jù)區(qū): Java虛擬機在執(zhí)行Java程序的過程中會將其管理的內(nèi)存劃分為若干個不同的數(shù)據(jù)區(qū)域,這些區(qū)域有各自的用途、創(chuàng)建和銷毀的時間,有些區(qū)域隨虛擬機進程的啟動而...

    shenhualong 評論0 收藏0

發(fā)表評論

0條評論

StonePanda

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<