Java 未使用的对象在';在堆栈中仍然可以看到什么?

Java 未使用的对象在';在堆栈中仍然可以看到什么?,java,garbage-collection,jvm,Java,Garbage Collection,Jvm,在以下示例中,有两种功能等效的方法: 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() {

在以下示例中,有两种功能等效的方法:

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(稍微)

我真的更喜欢不进行重新分配的代码,因为它更容易阅读和推理

更新:根据:

Java编译器或代码生成器可能会选择将不再使用的变量或参数设置为null,以使此类对象的存储可能更快地被回收


所以看起来GC可以声明仍然可见的对象。然而,我怀疑这种优化是在离线编译期间完成的(它会拧坏调试),最有可能的是JIT。

< P>不,因为您的代码可以想象地检索它并用它做一些事情,而抽象的JVM不考虑什么代码在前面。然而,一个非常、非常、非常聪明的优化JVM可能会提前分析代码,发现
s1
永远无法被引用,并对其进行垃圾收集。不过,你绝对不能指望这一点

在第一个语句中,字符串“s1”显然可用于return语句之前的垃圾收集

一点也不清楚。我认为你把“未使用”和“无法到达”混淆了。它们不一定是同一件事

从形式上讲,变量在其封闭范围终止之前是活动的,因此在此之前它不可用于垃圾收集


但是,“Java编译器或代码生成器可以选择将不再使用的变量或参数设置为null,以使此类对象的存储可能更快地被回收。”

VM可以自由优化代码,在方法退出之前将
s1
置为null(只要正确),因此,
s1
可能有资格更早地使用垃圾

然而,这几乎没有必要。许多方法调用必须在下一个GC之前发生;所有堆栈帧都已清除,无需担心特定方法调用中的特定局部变量


就Java语言而言,垃圾可以永远存在,而不会影响程序语义。这就是为什么JLS几乎不谈论垃圾的原因。

如果您谈论的是解释器,那么在第二种情况下,S1将保持“引用”,直到方法退出并卷起堆栈帧。(也就是说,在标准解释器中——GC完全可以使用来自方法验证的活跃度信息。此外(更可能的是),javac可以进行自己的活跃度分析,并在此基础上“共享”解释器插槽。)

然而,在JITC的情况下,即使是轻微的优化也可能会认识到S1未使用,并为S2回收该寄存器。也可能不是。GC将检查寄存器内容,如果S1已被重新用于其他用途,则旧的S1对象将被回收(如果未被引用)。如果未重用S1位置,则可能无法回收S1对象

“可能不会”,因为根据JVM的不同,JITC可能会也可能不会向GC提供对象引用在程序流中“活动”的位置的映射。如果提供此地图,可能会也可能不会精确识别S1的“活动范围”(最后一个参考点)的终点。许多不同的可能性


请注意,这种潜在的可变性并没有违反任何Java原则——GC不需要在尽可能早的时间回收对象,并且程序也没有实际的方法精确地对回收对象的时间敏感。

基本上,GC将堆栈帧和静态区域视为根。所以,如果对象是从任何堆栈帧引用的,则认为它是活动的。从活动堆栈框架中回收某些对象的问题是GC与应用程序(mutator)并行工作。您认为GC应该如何在方法进行时发现对象未使用?这将需要一个非常繁重和复杂的同步,事实上这将打破GC与mutator并行工作的想法。每个线程都可能在处理器寄存器中保留变量。为了实现您的逻辑,还应该将它们添加到GC根中。我甚至无法想象如何实施它

回答你的问题。如果您有任何逻辑产生了许多将来未使用的对象,请将其分离到一个不同的方法。这实际上是一个很好的做法


您还应该考虑JVM的int优化(如EJP指出的)。还有一个转义分析,它可能会阻止对象进行堆分配。但是依赖代码的性能是一种不好的做法

您对方法1还是方法2有疑问?删除另一种方法,因为它会使您的问题变得不那么清楚。当
消除死代码
时,则
新字符串(“s1”)甚至可能无法创建,因此不必进行垃圾收集。但是,虚拟机必须能够识别
新字符串(“s1”)
没有副作用。对于这样一个特殊的、众所周知的、高度优化的JDK类,如果这样做的话,我不会感到惊讶。@FabianBarney-虚拟机通常会(事实上,必须)假设