简体   繁体   English

当堆栈中仍然可见时,未使用的对象是否可用于垃圾收集?

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

In the following example there are two functionally equivalent methods: 在以下示例中,有两个功能相同的方法:

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;
    }
}

however in first( method1 ) of them string "s1" is clearly available for garbage collection before return statement. 然而,在第一( method1 )其中字符串“S1”显然是可进行垃圾回收之前return语句。 In second( method2 ) string "s1" is still reachable (though from code review prospective it's not used anymore). 在第二个( method2 )字符串“s1”仍然可以访问(虽然从代码审查预期它不再使用)。

My question is - is there anything in jvm spec which says that once variable is unused down the stack it could be available for garbage collection? 我的问题是 - 在jvm规范中是否有任何内容表明,一旦变量在堆栈中未使用,它可用于垃圾收集?

EDIT: Sometimes variables can refer to object like fully rendered image and that have impact on memory. 编辑:有时变量可以像完全渲染图像一样引用对象,并且会对内存产生影响。

I'm asking because of practical considerations. 我是因为实际考虑而问的。 I have large chunk of memory-greedy code in one method and thinking if I could help JVM (a bit) just by splitting this method into few small ones. 我在一个方法中有大量内存贪婪的代码,并且只是通过将此方法分成几个小方法来思考我是否可以帮助JVM(一点点)。

I really prefer code where no reassignment is done since it's easier to read and reason about. 我真的更喜欢没有重新分配的代码,因为它更容易阅读和推理。

UPDATE : per jls-12.6.1 : 更新 :按jls-12.6.1

Java compiler or code generator may choose to set a variable or parameter that will no longer be used to null to cause the storage for such an object to be potentially reclaimable sooner Java编译器或代码生成器可以选择设置将不再用于null的变量或参数,以使此类对象的存储可能更快地回收

So it looks like it's possible for GC to claim object which still visible. 所以看起来GC可以声明仍然可见的对象。 I doubt, however that this optimisation is done during offline compilation (it would screw up debugging) and most likely will be done by JIT. 我怀疑,然而这种优化是在离线编译期间完成的(它会搞砸调试),而且很可能是由JIT完成的。

No, because your code could conceivably retrieve it and do something with it, and the abstract JVM does not consider what code is coming ahead. 不,因为你的代码可以想象地检索它并用它做一些事情,而抽象的JVM不会考虑未来会有什么代码。 However, a very, very, very clever optimizing JVM might analyze the code ahead and find that there is no way s1 could ever be referenced, and garbage collect it. 但是,一个非常非常非常聪明的优化JVM可能会分析前面的代码并发现s1无法被引用,并且垃圾收集它。 You definitely can't count on this, though. 不过,你绝对不能指望这一点。

If you're talking about the interpreter, then in the second case S1 remains "referenced" until the method exits and the stack frame is rolled up. 如果您正在谈论解释器,那么在第二种情况下,S1保持“引用”,直到该方法退出并且堆栈帧被卷起。 (That is, in the standard interpreter -- it's entirely possible for GC to use liveness info from method verification. And, in addition (and more likely), javac may do its own liveness analysis and "share" interpreter slots based on that.) (也就是说,在标准解释器中 - GC完全有可能使用来自方法验证的活跃信息。而且,此外(并且更有可能),javac可以进行自己的活跃度分析并基于此分享“共享”解释器插槽。 )

In the case of the JITC, however, an even mildly optimizing one might recognize that S1 is unused and recycle that register for S2. 然而,在JITC的情况下,即使是稍微优化的人也可能认识到S1未被使用并且回收用于注册S2。 Or it might not. 或者它可能不会。 The GC will examine register contents, and if S1 has been reused for something else then the old S1 object will be reclaimed (if not otherwise referenced). GC将检查寄存器内容,如果S1已被重用于其他内容,则将回收旧的S1对象(如果没有另外引用)。 If the S1 location has not been reused then the S1 object might not be reclaimed. 如果尚未重用S1位置,则可能无法回收S1对象。

"Might not" because, depending on the JVM, the JITC may or may not provide the GC with a map of where object references are "live" in the program flow. “可能不会”,因为根据JVM,JITC可能会也可能不会向GC提供程序流中对象引用“活动”的映射。 And this map, if provided, may or may not precisely identify the end of the "live range" (the last point of reference) of S1. 并且该地图(如果提供)可以或可以不精确地识别S1的“有效范围”(最后一个参考点)的结束。 Many different possibilities. 许多不同的可能性

Note that this potential variability does not violate any Java principles -- GC is not required to reclaim an object at the earliest possible opportunity, and there's no practical way for a program to be sensitive to precisely when an object is reclaimed. 请注意,这种潜在的可变性并不违反任何Java原则 - GC不需要尽早回收对象,并且没有实际的方法使程序对回收对象时精确敏感。

VM is free to optimized the code to nullify s1 before method exit (as long as it's correct), so s1 might be eligible for garbage earlier. VM可以在方法退出之前自由优化代码以使s1无效(只要它是正确的),因此s1可能更早符合垃圾条件。

However that is hardly necessary. 然而,这几乎是不必要的。 Many method invocations must have happened before the next GC; 许多方法调用必须在下一个GC之前发生; all the stack frames have been cleared anyway, no need to worry about a specific local variable in a specific method invocation. 无论如何,所有堆栈帧都已被清除,无需担心特定方法调用中的特定局部变量。

As far as Java the language is concerned, garbages can live forever without impact program semantics. 就Java语言而言,垃圾可以永久存在而不会影响程序语义。 That's why JLS hardly talks about garbage at all. 这就是JLS几乎没有谈论垃圾的原因。

in first of them string "s1" is clearly available for garbage collection before return statement 在第一个字符串“s1”显然可用于返回语句之前的垃圾收集

It isn't clear at all. 目前尚不清楚。 I think you are confusing 'unused' with 'unreachable'. 我认为你将“未使用”与“无法访问”混为一谈。 They aren't necessarily the same thing. 它们不一定是一回事。

Formally speaking the variable is live until its enclosing scope terminates, so it isn't available for garbage collection until then. 从形式上讲,变量是有效的,直到其封闭范围终止,因此在此之前它不可用于垃圾收集。

However "a Java compiler or code generator may choose to set a variable or parameter that will no longer be used to null to cause the storage for such an object to be potentially reclaimable sooner" JLS #12.6.1 . 但是,“Java编译器或代码生成器可能会选择设置一个不再用于null的变量或参数,以使这种对象的存储能够更快地回收” JLS#12.6.1

Basically stack frames and static area are considered as roots by GC. 基本上堆栈帧和静态区域被GC视为根。 So if object is referenced from any stack frame its considered alive. 因此,如果从任何堆栈帧引用对象,则认为它是活动的。 The problem with reclaiming some objects from active stack frame is that GC works in parallel with application(mutator). 从活动堆栈帧中回收一些对象的问题是GC与应用程序(mutator)并行工作。 How do you think GC should find out that object is unused while method is in progress? 您如何看待GC在方法进行过程中发现该对象未被使用? That would require a synchronization which would be VERY heavy and complex, in fact this will break the idea of GC to work in parallel with mutator. 这将需要一个非常繁重和复杂的同步,事实上这将打破GC与mutator并行工作的想法。 Every thread might keep variables in processor registers. 每个线程都可以将变量保存在处理器寄存 To implement your logic, they should also be added to GC roots. 要实现您的逻辑,还应将它们添加到GC根目录中。 I cant even imagine how to implement it. 我甚至无法想象如何实现它。

To answer you question. 回答你的问题。 If you have any logic which produces a lot of objects which are unused in the future, separate it to a distinct method. 如果您有任何逻辑产生许多将来未使用的对象,请将其分离为不同的方法。 This is actually a good practice. 这实际上是一种很好的做法。

You should also take int account optimizations by JVM(like EJP pointed out). 您还应该通过JVM进行int帐户优化(如EJP指出)。 There is also an escape analysis, which might prevent object from heap allocation at all. 还有一个转义分析,可能会阻止对象进行堆分配。 But rely your codes performance on them is a bad practice 但依靠你的代码表现是一种不好的做法

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM