JVM如何使用堆栈来运行程序?有人告诉我,每行代码都被添加到堆栈中......但是如果堆栈是LIFO数据结构......这是不是意味着你的程序是向后运行的?或者我被告知错了?
编辑:
package test;
public class Testing {
public static void main(String args[])
{
method2();
}
public static void method() {
System.out.println(MyErrorHere);
}
public static void method2() {
method();
}
}
返回以下堆栈跟踪:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
MyErrorHere cannot be resolved to a variable
at test.Testing.method(Testing.java:11)
at test.Testing.method2(Testing.java:14)
at test.Testing.main(Testing.java:7)
每个被调用的方法都被添加到main方法中创建的堆栈中?
答案 0 :(得分:5)
我被告知每行代码都被添加到堆栈中......
不,事实并非如此。
JVM保留堆栈以跟踪数据。所有操作(例如添加和方法调用)都使用堆栈顶部的元素(最后一个)。我们来试试一个简单的例子。我通过将源创建为Test.java
,使用javac Test.java
进行编译,然后执行javah -c Test
来生成它,随后显示字节码。我强烈建议您在您的关卡中编写一个示例,查看字节码,然后尝试对其进行分析,或者在头脑中或使用笔记本进行分析,以便自己查看。
class Test {
public static void main(String[] args){
int a = 1;
int b = 2;
int c = a + b;
int d = c + 2;
int e = a + d + c;
System.out.println(e);
}
}
这就是字节码的样子:
0: iconst_1
1: istore_1
2: iconst_2
3: istore_2
4: iload_1
5: iload_2
6: iadd
7: istore_3
8: iload_3
9: iconst_2
10: iadd
11: istore 4
13: iload_1
14: iload 4
16: iadd
17: iload_3
18: iadd
19: istore 5
21: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
24: iload 5
26: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
29: return
首先,我们将1推入堆栈,然后将其从堆栈中弹出到局部变量中。然后我们再用2做同样的事情。此时堆栈帧是空的。
下一步将评估int c = a + b
。我们将两个局部变量(a
和b
)推入堆栈:
2
1
[BOTTOM]
并执行iadd
(称为整数添加)。这将使堆栈看起来如下:
3
[BOTTOM]
指令7和8 似乎是多余的,因为我们需要多次添加的结果。如果我们立即执行了第一次添加,那么该操作数将从堆栈中弹出并且无法再次添加。因此,它被存储(从堆栈中弹出),并再次推入堆栈以在其两个变量读取的第一个中使用。 9之后,堆栈看起来像
2
3
[BOTTOM]
添加,我们存储结果(d
)。
然后我们再次加载指令13,d
加载指令14:
5 (value of d)
1 (value of a)
[BOTTOM]
我们补充说:
6 (a+d)
[BOTTOM]
然后将c
推送到指令17的堆栈中。再次添加获取e
的值,该值存储在局部变量5中。
getstatic #2
用于将类型System.out
的{{1}}推送到堆栈。 PrintStream
引用类文件中的常量池条目。然后,将用于#2
的参数被压入堆栈:
System.out.println(int)
然后,9
PrintStream object that we got by evaluating System.out
[BOTTOM]
invokevirtual #3
引用方法名称,用于调用PrintStream的方法#3
。堆栈包含将传递的参数,以及调用此方法的实例。