看了下这个问题,其实自己也是深有感触,相信大部分编程初学者都会有这样的疑问,会好奇自己编写的代码是如何run起来的,刚学JVM内存模型的时候会好奇自己编写的代码在内存中是怎样子的,当初我也有这种疑问。
要回答这个问题需要对Java代码是如何被JVM运行的,并且会JVM内存模型,内存分配有一定了解。
Java代码是如何run起来的
在使用现代IDE开发过程中,从我们编写代码到运行结果一切显得很简单,而其中过程究竟是怎样的呢?我整理了一下,大致的步骤如下:
- 编译,java代码编译成字节码class文件
- 加载,将class文件加载到JVM中,存放在方法区(Method Area)
- 执行,执行以方法为单位,一个方法对应一个Java栈(字节码到机器码)
我们知道,平时我们编写的J代码是用Java语言,而Java代码需要通过编译成字节码才能被JVM所运行,这也是Java跨平台的原因,由JVM屏蔽底层差异,而字节码是无法被操作直接执行的,所以,JVM需要把字节码翻译成机器码,这个时候才算是真正的run起来
JVM如何运行成员变量int a=1;
例如,有这样的代码:
public class Foo {
private int a = 1;
public int getValue(){
return a;
}
}
反编译,javap -v -p -l Foo.class
,可以知道:
Classfile /D:/execise/demo/target/test-classes/com/sevenlin/demo/Foo.class
Last modified 2019-9-12; size 383 bytes
MD5 checksum 37b0e24496211bdc65a4579b015a83f1
Compiled from "Foo.java"
public class com.sevenlin.demo.Foo
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #4.#18 // java/lang/Object."<init>":()V
#2 = Fieldref #3.#19 // com/sevenlin/demo/Foo.a:I
#3 = Class #20 // com/sevenlin/demo/Foo
#4 = Class #21 // java/lang/Object
#5 = Utf8 a
#6 = Utf8 I
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lcom/sevenlin/demo/Foo;
#14 = Utf8 getValue
#15 = Utf8 ()I
#16 = Utf8 SourceFile
#17 = Utf8 Foo.java
#18 = NameAndType #7:#8 // "<init>":()V
#19 = NameAndType #5:#6 // a:I
#20 = Utf8 com/sevenlin/demo/Foo
#21 = Utf8 java/lang/Object
{
private int a;
descriptor: I
flags: ACC_PRIVATE
public com.sevenlin.demo.Foo();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: iconst_1
6: putfield #2 // Field a:I
9: return
LineNumberTable:
line 7: 0
line 9: 4
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this Lcom/sevenlin/demo/Foo;
public int getValue();
descriptor: ()I
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #2 // Field a:I
4: ireturn
LineNumberTable:
line 12: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/sevenlin/demo/Foo;
}
SourceFile: "Foo.java"
从中可以看出,如果我们执行代码new Foo().getValue()
的话
- 成员变量a在Foo构造函数中被初始化
- Foo实例会在堆中申请内存
iconst_1
常量1通过putfield
操作初始化成员变量a
那么,至此这个问题可以如此回答:
成员变量 int a = 1, a作为变量名,在JVM中是以代码的形式存在,存放在方法区,当有线程执行到该代码的时候,会加载该代码进行执行,而1作为参数 a 的值在运行时存放在堆内存中,a指向该内存
如果int a=1;
作为局部变量存在呢?
public class Foo {
public int getValue(){
int a = 1;
return a;
}
}
反编译如下(截取getValue部分)
public int getValue();
descriptor: ()I
flags: ACC_PUBLIC
Code:
stack=1, locals=2, args_size=1
0: iconst_1
1: istore_1
2: iload_1
3: ireturn
LineNumberTable:
line 11: 0
line 12: 2
LocalVariableTable:
Start Length Slot Name Signature
0 4 0 this Lcom/sevenlin/demo/Foo;
2 2 1 a I
可以知道,对于局部变量,a存在局部变量表中,在这个例子中,getValue()
的声明周期就是该方法对应的Java栈,执行到a=1
时会将iconst_1
放到操作数栈,然后通过istore_1
存放局部变量变中1的位置,而从中可以知道,a的类型为int,所以在变量槽(Solt)中存在的就是数值1
所以,int a=1
为局部变量的时候,这个时候a同样存在方法区的代码中,运行时a存在该方法对应的栈帧的局部变量表中,而该变量表中a的值为1,所以1存在栈内存中。
总结
对于变量a
,不能说存在哪儿,要理解变量,变量只是编程中一个抽象的概念,它只是对内存空间的一个引用,能够通过变量访问对应的数值,而该数值才是我们所关心的数据
注意:本文归作者所有,未经作者允许,不得转载