簡體   English   中英

JVisualVM / JConsole中的System.gc()vs GC按鈕

[英]System.gc() vs GC button in JVisualVM/JConsole

我目前正在測試處理XML模式的概念證明原型,並圍繞一個非常消耗內存的樹自動機外部庫構建(我已經從中獲得了資源),我想繪制“真實峰值”(堆) )隨着模式大小的增加(使用的指標符合我的宗旨,並且不影響問題),或者至少是合理地近似,不同運行的內存消耗。

為了給出一個數量級,對於實際峰值為100MB的運行(我測試了它運行了幾次完全相同的輸入/參數配置,迫使帶有-Xmx和-Xms的jvm內存減小了值, 在線程“ main” java.lang.OutOfMemoryError:GC開銷限制超過 <100MB,具有穩定和可重復的結果)它占據了1.1GB左右的位置,這就是為什么獲取實數對我來說非常重要的原因,因為它們相差很大!

在過去的10天里,我一直在網上和stackoverflow上閱讀問題,實際上我知道的是:

  1. System.gc()“建議” GC運行,不會以任何方式強制執行,因此無法依靠它來檢測內存使用高峰

  2. 通常建議對對象的占用進行計數(為此我看到了SizeOf項目,即使它不符合我的需求,我也嘗試並且可以正常工作),這對我來說是不可行的,因為由於創建了a而導致大量內存分配很多用不同方法的集合(集合,列表和映射)迭代器,被稱為非常高的次數(例如,我記得,每次運行數百萬分鍾,每次迭代數百萬次),因此很難檢測到所有涉及的對象並執行總和(我在幾天內用內存消耗圖調試了許多運行,而無法僅識別單個瓶頸)

  3. 無法輕松獲得方法的內存占用(表示為對象內存分配的峰值)

事實是,我自己經歷了System.gc()調用不可靠的情況(例如,由於確實調用了GC,因此,相同配置的不同運行,在System.gc()之后讀取了不同的內存),但是我按下JVisualVM或Jconsole中的“ GC按鈕”,它永遠不會失敗或拒絕運行GC。

所以我的問題是:調用他們對該按鈕的實現(我還沒有嘗試過,但是就我到目前為止所讀的內容而言,將jconsole.jar與Attach api一起使用似乎是可行的)與直接調用System.gc()有所不同從我的代碼,從而解決我的問題? 如果不是,您如何解釋該按鈕的“確定性行為”?

到目前為止,給定了10個遞增的方案大小,我對實際內存峰值進行了一些手動測試(對於這種測量,方案是從單個“復雜性參數”自動生成的),並且繪制了期望的曲線(如果我不能這樣做的話)獲得更好的解決方案,我想將代碼作為外部jar運行-Xmx / -Xms稍小於預期內存峰值的預測,在外部進程ErrorStream中捕獲OutMemoryException並重新啟動內存,直到完成運行已完成。 (如果幼稚的內存預測不夠魯棒,我將應用適當的機器學習技術)。 我知道這不是一個很好的解決方案,但是在我的情況下(學術界),我有能力花一些額外的時間進行這些測量。 如果您對此蠻力方法有其他建議或改進,歡迎與您分享。

系統信息(機器是Fedora 17,64位):

Java版本“ 1.7.0_04” Java™SE運行時環境(內部版本1.7.0_04-b20)Java HotSpot(TM)64位服務器VM(內部版本23.0-b21,混合模式)

在此先感謝,亞歷山德羅

據我所知,Jconsole或任何其他工具僅使用System.gc()。 沒有其他選擇。 眾所周知,java告訴每個人不要依賴System.gc(),但這並不意味着它根本不起作用。

因此,進入您的查詢時,您似乎擔心如何直接按下該按鈕會調用GC,而Java仍然說System.gc僅“建議”調用GC。 我說,該按鈕還調用System.gc(),並且它僅“建議” java嘗試進行GC,並且發生了Java決定在當時自行執行GC的情況(它不能保證,但Java會這樣做)。

因此,為了證明這一事實,我創建了一個簡單的程序,該程序僅創建對象的負載。 它已用“ System.gc()”注釋行。 現在嘗試先用注釋的System.gc()運行該程序,然后取消注釋System.gc()。 確保將VM參數提供為-verbose:gc -XX:+ PrintGCTimeStamps -XX:+ PrintGCDetails。

package ravi.tutorial.java.gc;

/**
 * Just to test GC. RUn with below VM arguments.
 * 
 * -verbose:gc -XX:+PrintGCTimeStamps -XX:+PrintGCDetails
 * 
 * 
 * @author ravi.k
 * 
 */
public class TestGC {

    public static A a;

    /**
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {

        for (int i = 0; i < 100; i++) {
            populateObjects();
            System.out.println("population done for batch: " + i);
        }

    }

    public static void populateObjects() {
        for (int i = 0; i < 100000; i++) {
            a = new A("A");
        }
        //System.gc();
    }

}

class A {
    String s;

    public A(String s) {
        this.s = s;
    }
}

這里部分輸出我的機器。

Commened System.gc():在這里,jre隨意調用了GC。

population done for batch: 0
population done for batch: 1
population done for batch: 2
population done for batch: 3
population done for batch: 4
population done for batch: 5
population done for batch: 6
population done for batch: 7
population done for batch: 8
population done for batch: 9
0.332: [GC 0.332: [ParNew: 17024K->410K(19136K), 0.0024479 secs] 17024K->410K(83008K), 0.0025219 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
population done for batch: 10
population done for batch: 11
population done for batch: 12
population done for batch: 13
population done for batch: 14
population done for batch: 15
population done for batch: 16
population done for batch: 17
population done for batch: 18
population done for batch: 19
0.344: [GC 0.344: [ParNew: 17434K->592K(19136K), 0.0011238 secs] 17434K->592K(83008K), 0.0011645 secs] [Times: user=0.00 sys=0.01, real=0.00 secs] 
population done for batch: 20
population done for batch: 21
population done for batch: 22
population done for batch: 23
population done for batch: 24
population done for batch: 25
population done for batch: 26
population done for batch: 27
population done for batch: 28
population done for batch: 29
population done for batch: 30
0.353: [GC 0.353: [ParNew: 17616K->543K(19136K), 0.0011398 secs] 17616K->543K(83008K), 0.0011770 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
population done for batch: 31
population done for batch: 32
population done for batch: 33

未注釋的System.gc():在此,每批都將調用GC。 現在System.gc()僅建議使用GC,但是Java當時選擇運行GC。 這與其他工具中的魔術GC按鈕完全相同:)

0.337: [Full GC (System) 0.337: [CMS: 0K->400K(63872K), 0.0219250 secs] 3296K->400K(83008K), [CMS Perm : 4423K->4422K(21248K)], 0.0220152 secs] [Times: user=0.04 sys=0.00, real=0.02 secs] 
population done for batch: 0
0.364: [Full GC (System) 0.364: [CMS: 400K->394K(63872K), 0.0161792 secs] 2492K->394K(83008K), [CMS Perm : 4425K->4425K(21248K)], 0.0162336 secs] [Times: user=0.01 sys=0.00, real=0.02 secs] 
population done for batch: 1
0.382: [Full GC (System) 0.382: [CMS: 394K->394K(63872K), 0.0160193 secs] 2096K->394K(83008K), [CMS Perm : 4425K->4425K(21248K)], 0.0160834 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
population done for batch: 2
0.399: [Full GC (System) 0.399: [CMS: 394K->394K(63872K), 0.0160866 secs] 2096K->394K(83008K), [CMS Perm : 4425K->4425K(21248K)], 0.0161489 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 
population done for batch: 3
0.417: [Full GC (System) 0.417: [CMS: 394K->394K(63872K), 0.0156326 secs] 2096K->394K(83008K), [CMS Perm : 4425K->4425K(21248K)], 0.0156924 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 
population done for batch: 4
0.434: [Full GC (System) 0.434: [CMS: 394K->394K(63872K), 0.0157274 secs] 2096K->394K(83008K), [CMS Perm : 4425K->4425K(21248K)], 0.0157897 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
population done for batch: 5

要添加更多,就像線程一樣。 無法保證線程何時運行,但是只要我們編寫任何示例線程程序,線程都會在該時間運行。 因此,我們不應該怪怪Java在線程啟動后如何運行:)。 Java只說不依賴這些東西,但是它們確實起作用。 同樣,盡管它們在某些情況下有效,但這並不意味着它們每次都會起作用。 甚至那些jconsole工具也可能無法執行GC,只是我們從未見過。

我對這種簡單的方法有很多積極的經驗:

System.gc();
Thread.sleep(500);
System.gc();

由於對象完成問題,一次GC運行通常是不夠的,因為在對象完成過程中可能會復活對象。 因此,在第二次GC運行中會釋放更多的內存。

請注意,此方法以及其他一些看似“更智能”的方法都是啟發式的,並且完全取決於JVM的確切版本,包括其GC配置。 但是在很多情況下,您對通用性不會有太大的興趣:如果現在可以通用並允許您進行測量,那么這就是要走的路。

1)System.gc()“建議” GC運行,不會以任何方式強制執行,因此無法依靠它來檢測內存使用高峰

那就是規范所說的,但是如果您使用OpenJDK或HotSpot,它將始終執行Full GC,除非您將其關閉。

通常建議計算對象占用

我建議使用商用內存分析器。 我會讓JVM的最大容量為8 GB,然后看看它會嘗試使用多少。 在那之后,我將根據您對它是否想要使用它的判斷來增加或減少它。

無法輕松獲得方法的內存占用(表示為對象內存分配的峰值)

方法使用的唯一內存在堆棧上。 您可以跟蹤在一個方法中創建了多少個對象(計數,類,大小),但是這些對象不屬於該方法,並且可以在任何地方使用,即使該方法返回后也可以使用。

如果不是,您如何解釋該按鈕的“確定性行為”?

我將其歸結為主觀分析。 ;)

理想情況下,您應該以使其有效運行所需的最小內存的2-3倍運行JVM。 嘗試節省幾百個不到1美元的MB並不總是有用的。 ;)

您可以像這樣強制使用GC。

private static void force_gc()
{
    Object obj = new Object();
    WeakReference<Object> ref = new WeakReference<Object>(obj);
    obj = null;
    while (ref.get() != null)
    {
        Log.d(LOGTAG, "Forcing gc() ...");
        System.gc();
    }
}

除此之外...我很想知道這個問題的去向。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM