簡體   English   中英

當堆棧中仍然可見時,未使用的對象是否可用於垃圾收集?

[英]Is unused object available for garbage collection when it's still visible in stack?

在以下示例中,有兩個功能相同的方法:

public class Question {

    public static String method1() {
        String s = new String("s1");
        // some operations on s1
        s = new String("s2");
        return s;
    }

    public static String method2() {
        final String s1 = new String("s1");
        // some operations on s1
        final String s2 = new String("s2");
        return s2;
    }
}

然而,在第一( method1 )其中字符串“S1”顯然是可進行垃圾回收之前return語句。 在第二個( method2 )字符串“s1”仍然可以訪問(雖然從代碼審查預期它不再使用)。

我的問題是 - 在jvm規范中是否有任何內容表明,一旦變量在堆棧中未使用,它可用於垃圾收集?

編輯:有時變量可以像完全渲染圖像一樣引用對象,並且會對內存產生影響。

我是因為實際考慮而問的。 我在一個方法中有大量內存貪婪的代碼,並且只是通過將此方法分成幾個小方法來思考我是否可以幫助JVM(一點點)。

我真的更喜歡沒有重新分配的代碼,因為它更容易閱讀和推理。

更新 :按jls-12.6.1

Java編譯器或代碼生成器可以選擇設置將不再用於null的變量或參數,以使此類對象的存儲可能更快地回收

所以看起來GC可以聲明仍然可見的對象。 我懷疑,然而這種優化是在離線編譯期間完成的(它會搞砸調試),而且很可能是由JIT完成的。

不,因為你的代碼可以想象地檢索它並用它做一些事情,而抽象的JVM不會考慮未來會有什么代碼。 但是,一個非常非常非常聰明的優化JVM可能會分析前面的代碼並發現s1無法被引用,並且垃圾收集它。 不過,你絕對不能指望這一點。

如果您正在談論解釋器,那么在第二種情況下,S1保持“引用”,直到該方法退出並且堆棧幀被卷起。 (也就是說,在標准解釋器中 - GC完全有可能使用來自方法驗證的活躍信息。而且,此外(並且更有可能),javac可以進行自己的活躍度分析並基於此分享“共享”解釋器插槽。 )

然而,在JITC的情況下,即使是稍微優化的人也可能認識到S1未被使用並且回收用於注冊S2。 或者它可能不會。 GC將檢查寄存器內容,如果S1已被重用於其他內容,則將回收舊的S1對象(如果沒有另外引用)。 如果尚未重用S1位置,則可能無法回收S1對象。

“可能不會”,因為根據JVM,JITC可能會也可能不會向GC提供程序流中對象引用“活動”的映射。 並且該地圖(如果提供)可以或可以不精確地識別S1的“有效范圍”(最后一個參考點)的結束。 許多不同的可能性

請注意,這種潛在的可變性並不違反任何Java原則 - GC不需要盡早回收對象,並且沒有實際的方法使程序對回收對象時精確敏感。

VM可以在方法退出之前自由優化代碼以使s1無效(只要它是正確的),因此s1可能更早符合垃圾條件。

然而,這幾乎是不必要的。 許多方法調用必須在下一個GC之前發生; 無論如何,所有堆棧幀都已被清除,無需擔心特定方法調用中的特定局部變量。

就Java語言而言,垃圾可以永久存在而不會影響程序語義。 這就是JLS幾乎沒有談論垃圾的原因。

在第一個字符串“s1”顯然可用於返回語句之前的垃圾收集

目前尚不清楚。 我認為你將“未使用”與“無法訪問”混為一談。 它們不一定是一回事。

從形式上講,變量是有效的,直到其封閉范圍終止,因此在此之前它不可用於垃圾收集。

但是,“Java編譯器或代碼生成器可能會選擇設置一個不再用於null的變量或參數,以使這種對象的存儲能夠更快地回收” JLS#12.6.1

基本上堆棧幀和靜態區域被GC視為根。 因此,如果從任何堆棧幀引用對象,則認為它是活動的。 從活動堆棧幀中回收一些對象的問題是GC與應用程序(mutator)並行工作。 您如何看待GC在方法進行過程中發現該對象未被使用? 這將需要一個非常繁重和復雜的同步,事實上這將打破GC與mutator並行工作的想法。 每個線程都可以將變量保存在處理器寄存 要實現您的邏輯,還應將它們添加到GC根目錄中。 我甚至無法想象如何實現它。

回答你的問題。 如果您有任何邏輯產生許多將來未使用的對象,請將其分離為不同的方法。 這實際上是一種很好的做法。

您還應該通過JVM進行int帳戶優化(如EJP指出)。 還有一個轉義分析,可能會阻止對象進行堆分配。 但依靠你的代碼表現是一種不好的做法

暫無
暫無

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

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