Java 添加作用域{}括号以使变量超出作用域是否与调用和结束函数或某种空循环具有相同的效果?
我想知道是否有可能让变量超出范围,而只在某段代码周围添加括号,这与让变量超出范围的其他方法是否有任何不同: 0)添加括号:Java 添加作用域{}括号以使变量超出作用域是否与调用和结束函数或某种空循环具有相同的效果?,java,scope,stack,brackets,Java,Scope,Stack,Brackets,我想知道是否有可能让变量超出范围,而只在某段代码周围添加括号,这与让变量超出范围的其他方法是否有任何不同: 0)添加括号: private void methodExample() { { int example = 5; } //variable "example" out of scope } 1) 函数调用: private void methodExample() { justMakeItAMethod(); //
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
}
每个示例中,变量超出堆栈范围的方式是否不同
我想到这一点的原因:[免责声明我知道这是一个过早优化的案例]我有一个低级函数,其中我有许多不同的独立代码小部分,功能略有不同,我认为这不足以使它成为一个需要函数调用开销的不同函数,但是已经有足够多的部分使变量超出了范围。在java对象中,没有用于删除它们的Distructor或方法,因此不能让它们释放内存 无法保证对象在离开其声明范围且不存在对其的有效引用时将被回收。在像java这样为您管理内存的语言中,您提供的代码不会对所使用的内存产生影响 所以在java中,最好避免这种构造。这可能会导致错误和更难理解的代码 但是,我不确定这是否会像一个超出范围的函数那样从堆栈中删除变量 没有 引擎盖下面发生了什么 运行时什么都没有 这与超出范围的函数不同吗 对。当函数返回时,堆栈弹出,函数中定义的所有变量都不存在。包括像您所询问的内部范围中的那些 或者。。当。。循环结束了吗 这只是内部范围的另一个例子。运行时不会发生任何事情
如果堆栈槽被重用,那么先前的值当然会消失,这会导致它引用的对象的GC。然而,仅仅超出范围并不会导致这一点,或者确实是什么原因。不可能。内部
}
字节码没有字节码指令,字节码调查显示了相当有趣的结果。局部变量表(在堆栈框架内)具有每个局部变量的插槽。因此,对于代码块中的变量,插槽将被下一个变量覆盖。这意味着一旦执行完块,变量就不再可用
代码:
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 行为可能会有所不同,这取决于您使用的是解释帧还是JIT编译帧。在JIT编译的框架中,在最后一次使用变量后,即使对象仍在范围内,也可以将其标记为自由。例如,见。在解释帧中,即使在作用域结束后,它仍将驻留在堆栈帧变量槽中。另一方面,编译器重用在作用域结束时释放的变量插槽。这可能导致解释帧的有趣结果:
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
消息会更早出现
在任何情况下,“scope end”在字节码中编译为空,因此JVM不知道在哪里关闭花括号。如果您认为您的代码将在解释帧中执行,那么在使用后将
null
分配给变量可能比使用block更安全,并且希望在它之后创建新的变量。虽然若您有一部分方法执行时间很长,并且不使用以前创建的变量,那个么最好将您的方法拆分为更小的部分。在JIT编译的框架中,您根本不应该对此进行优化:JIT编译器是智能的。不要在此处发布文本的结构。浪费你的时间和我们的带宽。发布文本。你从代码中得到了什么确切的错误信息?它是什么?我的猜测是,这实际上只是一个警告,你没有使用变量。最后一行是无效的,因为变量确实超出了范围。@Joop没有人能理解这幅图的要点。全是黑色的。浪费你的时间和我的钱。不要这样做。这正是为什么发布文本和解释会比发布图像更好的原因……在任何情况下,您都不需要让我们相信变量超出了范围。JLS是这样说的。LVT只用于帮助调试(也就是说,您现在可以在调试器的变量选项卡中看到哪些变量是有效的)。对于代码执行来说,它是完全不必要的,可以通过javac-g:none
@TagirValeev关闭,因此,JVM不使用LVT?。。它不是元数据的一部分?@TagirValeev-那么为什么JVM规范说每个stackframe都有3个组件:1。操作数堆栈。2.对类常量池的引用。3局部变量表。当然,stackframe包含局部变量,但它们仅通过解析方法签名(用于输入参数)、使用最大局部值(用于预分配表)和执行代码(例如,在遇到astore
指令时创建新条目)创建。类文件中的LVT不用于此目的。@TagirValeev-Oh。谢谢:)请解释一下。。如果你认为这个答案是错误的,请告诉我们为什么?我不是说它是错误的。但我认为这不是一个很好的答案,因为它没有提供任何解释或背景。@popovitsj这种说法完全是错误的。@EJP-但Tagir说JVM不会使用LVT(检查对我答案的评论)。现在,我不太确定。因为您的最后一段指出JVM正在使用LVT。。恳求