添加范围{}括号以使变量超出范围是否与调用和结束函数或某种空循环具有相同的效果?

时间:2015-05-08 08:16:36

标签: java scope stack brackets

我想知道是否有可能让变量超出范围而只是在某些代码周围添加括号,以及它是否与让变量超出范围的其他方式有所不同:

0)添加括号:

private void methodExample() {
    {
        int example = 5;
    }
    //variable "example" out of scope
}

1)函数调用:

private void methodExample() {        
    justMakeItAMethod();
    //variable "example" out of scope
}

private void justMakeItAMethod() {
        int example = 5;
}

2)循环结束如:

private void methodExample() {
    do {
        int example = 5;
    } while (false);
    //variable "example" out of scope
}

变量超出范围的方式是否与堆栈不同?

我之所以想到这个原因: [免责声明我知道这是一个过早优化的情况]我有一个低级函数,其中我有许多不同的小独立部分代码,功能略有不同,不够我的意见是使它成为一个需要函数调用开销的不同函数,但是有足够的部分而不是变量已经超出了范围。

4 个答案:

答案 0 :(得分:4)

行为可能会有所不同,具体取决于您是否具有已解释或JIT编译的帧。在JIT编译的框架中,对象可以在变量的最后一次使用后被标记为空闲,即使它仍在范围内。例如,请参阅this answer。在解释的帧中,即使在范围结束后,它仍将驻留在堆栈帧变量槽中。另一方面,编译器重用可变插槽,这些插槽作为作用域结束而被释放。这可以为解释的帧带来有趣的结果:

public class LVTest {
    @Override
    protected void finalize() {
        System.out.println("Finalized");
    }

    public static void main(String[] args) throws InterruptedException {
        {
            LVTest t = new LVTest();
        }
        System.out.println("GC!");
        System.gc();
        Thread.sleep(1000);
        System.out.println("GC!");
        System.gc();
        Thread.sleep(1000);
        System.out.println("GC!");
        System.gc();
        Thread.sleep(1000);
        System.out.println("Assign new var!");
        int a = 5;
        System.out.println("GC!");
        System.gc();
        Thread.sleep(1000);
        System.out.println("Finish");
    }
}

输出如下:

GC!
GC!
GC!
Assign new var!
GC!
Finalized
Finish

因此,无论我们收集垃圾多少次,我们的超出范围的对象都没有最终确定。但是在我们分配了一个重用相同变量槽的新变量之后,我们终于可以释放原始变量了。如果这个方法是JIT编译的,那么Finalized消息就会更早出现。

在任何情况下,“范围结束”在字节码中编译为空,因此JVM不知道您关闭大括号的位置。如果您认为您的代码将在解释的框架中执行,那么在使用之后将null分配给变量可能比使用块更安全,并希望在它之后创建新变量。虽然如果你有一部分方法执行很长时间并且不使用以前创建的变量,那么将方法拆分为较小的部分会好得多。在JIT编译的框架中,你根本不应该优化它:JIT编译器很聪明。

答案 1 :(得分:2)

Bytecode调查显示了相当有趣的结果。局部变量表(在堆栈框架内)具有每个局部变量的槽。因此,如果代码块中存在变量,则插槽将被下一个变量覆盖。这意味着一旦完成块的执行,变量就不再可用了。

代码:

public static void main(String[] args) {

    {
        int x ;
        x=5;
        System.out.println(x);
    }

    int y = 1;
    System.out.println(y);
    {
        int x ;
        x=5;
        System.out.println(x);
    }
    int z = 2;
    System.out.println(z);

}


  LocalVariableTable:
    Start  Length  Slot  Name   Signature
        0      37     0  args   [Ljava/lang/String;
        2       7     1     x   I // in block
       11      26     1     y   I  
       20       7     2     x   I // in block
       29       8     2     z   I  

因此,将一些信息发送到JVM。

答案 2 :(得分:1)

  

但是我不确定这是否也会像超出范围的函数一样从堆栈中删除变量呢?

没有。

  

引擎盖下发生了什么?

在运行时没什么。

  

这是否与超出范围的功能

不同

是。当函数返回时,弹出堆栈并且函数内定义的所有变量都不再存在。包括像你要问的内部范围中的那些。

  

或a .. while / for ..循环结束?

这只是内部范围的另一种情况。在运行时没有任何事情发生。

如果堆栈槽被重用,前一个值当然会消失,这可能导致GC引用它的对象。然而,只是超出范围不会导致这种情况,或者确实是任何事情。它不能。内部}没有字节码指令。

答案 3 :(得分:-1)

在java对象中,没有用于删除它们的析构函数或方法,因此无法让它们释放内存。

无法保证对象在离开其声明范围时将被回收,并且不存在对它的有效引用。在像java这样管理内存的语言中,你给出的代码对使用的内存没有任何影响。

所以在java中你最好避免使用这样的结构。它可能导致错误并且更难理解代码。