JVM堆栈如何工作?

时间:2014-09-23 01:00:28

标签: java jvm

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方法中创建的堆栈中?

1 个答案:

答案 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。我们将两个局部变量(ab)推入堆栈:

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。堆栈包含将传递的参数,以及调用此方法的实例。