国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

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

StonePanda / 1569人閱讀

摘要:第一個大陡坡是應用發布,老年代內存占比下降,很正常。但此時老年代內存使用占比。因為后期并不會引發。可以看出,由于到達時候,觸發了一次和一次。但觸發時,占比并沒用明顯的規律。得出,擴容導致這個說法,其實是不準確的。

轉載請注明原文鏈接:https://www.jianshu.com/p/468...

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

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

之后毛老師發來一張圖。

圖片展示了老年代內存占用情況。

第一個大陡坡是應用發布,老年代內存占比下降,很正常。

第二個小陡坡,老年代內存占用突然下降,應該是發生了老年代 GC。

但奇怪的是,此時老年代內存占用并不高,發生 GC 并不是正常現象。

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

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

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

而 CMS 觸發的條件是:

老年代內存使用占比達到 CMSInitiatingOccupancyFraction,默認為 92%,

毛老師設置的是 75%。

-XX:CMSInitiatingOccupancyFraction = 75

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

接著分析內存狀況。

毛老師發現在老年代發生 GC 時,Metaspace 的內存占用也一起下降。

于是懷疑是 Metaspace 占用達到了設置的參數 MetaspaceSize,發生了 GC。

查看 JVM 參數設置,MetaspaceSize 參數被設置為128m。

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

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

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

此時,引發出另一個問題:

Metaspace 發生 GC,為何會引起老年代 GC。

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

其中有幾個關鍵點:

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

Metaspace 擴容到 -XX:MetaspaceSize 參數指定的量,就會發生 FGC。

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

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

其中的關鍵點是:

如果老年代設置了 CMS,則 Metasapce 擴容引起的 FGC 會轉變成一次 CMS。

查看毛老師配置的 JVM 參數,果然設置了 CMS GC。

-XX:+UseConcMarkSweepGC

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

從監控來看,設置 -XX:MaxMetaspaceSize = 256m 已經足夠。

因為后期并不會引發 CMS GC。

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

Metaspace 分配和擴容有什么規律?

JDK 1.8 中的 Metaspace 和 JDK 1.7 中的 Perm 區有什么區別?

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

如何制造 Metasapce 內存占用上升?

關于這個問題一和問題二,阿飛Javaer 已經解釋的比較清楚。

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

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

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

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

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

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

JVM 在啟動的時候會根據 -XX:PermSize 初始化分配一塊連續的內存塊。

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

關于 Metaspace,JVM 還提供了其余一些設置參數。

可以通過以下命令查看。

java -XX:+PrintFlagsFinal -version | grep Metaspace

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

問題三

Metaspace 占用到達 -XX:MetaspaceSize 會引發什么?

已經知道,當老年代回收設置成 CMS GC 時,會觸發一次 CMS GC。

那么如果不設置為 CMS GC,又會發生什么呢?

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

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

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

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

于是,設置 -XX:MetaspaceSize = 40m,期望發生一次 GC。

從 GC log 中,可以找到以下關鍵日志。

[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 時候,觸發了一次 YGC 和一次 Full GC。

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

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

問題四

如何人工模擬 Metaspace 內存占用上升?

Metaspace 是 JDK 1.8 之后引入的一個區域。

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

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

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

于是想到,使用 CGlib 動態代理,生成被代理類的子類。

簡單的 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();
        // 設置需要創建子類的類
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        enhancer.setUseCache(false);
        // 通過字節碼技術動態創建子類實例
        return enhancer.create();
    }

    // 實現MethodInterceptor接口方法
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("前置代理");
        // 通過代理類調用父類中的方法
        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++) {
        //通過生成子類的方式創建代理類
        SayHello proxyTmp = (SayHello) proxy.getProxy(SayHello.class);
        proxyTmp.say();
    }
}

應用啟動完畢后,請求 /getProxy 接口,發現 Metaspace 空間占用上升。

從堆 Dump 中也可以發現,有很多被 CGlib 所代理的 SayHello 類對象。

代理類對應的 java.lang.Class 對象分配在堆內,類的描述信息在 Metaspace 中。

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

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

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

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

但會出現以下字樣。

Full GC (Last ditch collection)

此外,還有一個問題。

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

當 Metaspace 內存占用達到 -XX:MetaspaceSize 時,會發生 Full GC。

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

那么,第二次觸發 Full GC 的條件是?

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

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

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

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

jstat FGC 次數一直都是 1。

此外,使用 GClib 動態生成類,Metaspace 繼續擴容,到一定程度,觸發了 Full GC。

但觸發 FGC 時,Metaspace 占比并沒用明顯的規律。

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

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

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

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

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

文章有一句話:

從上述分析中可以發現,gc操作的入口都位于GenCollectedHeap::do_collection方法中。
不同的參數執行不同類型的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 的大小。

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

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

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

參考資料

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

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

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

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/76765.html

相關文章

  • jvm內存分配策略和性能監控

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

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

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

    Joyven 評論0 收藏0
  • 系統優化怎么做-Tomcat優化

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

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

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

    shenhualong 評論0 收藏0

發表評論

0條評論

StonePanda

|高級講師

TA的文章

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