前三个sp我们详细的讲解了一个对象被创建出来之后发生的一切,包括GC收集器运行过程,但是java对象本身从何而来,或者说,一个正常完整的java程序是如何完整运行的?
在这一篇,我们将会以一段简单的代码实例作为范例,详细讲解一段程序在执行时,JVM底层的空间分配
class Student {
int age;
String name;
Computer computer;
public void play(){
System.out.printf("我在玩!");
}
public void studyByComputer(){
System.out.printf("我在用我家的"+computer.Brand+"牌的电脑学习!");
}
}
class Computer{
String Brand;
}
public class Demo1{
public static void main(String args[]){
Student stu1 = new Student();
stu1.name = "名字还没想好";
stu1.age = 24;
Computer com1 = new Computer();
com1.Brand = "联想";
stu1.computer = com1;
stu1.play();
stu1.studyByComputer();
}
}
如果有用过记事本编程的朋友应该都知道,像如上的程序在eclipse等开发环境中直接Run 的话,在底层相当于在cmd中输入代码:
//命令javac编译器将Demo1.java文件编译成字节码Class文件
javac Demo1.java
//命令java执行编译后的Demo1Class文件
java Demo1
对于我们的JVM来说,java Demo1是要求其执行Demo1类
因此,此时jvm将会加载该类
之后,执行引擎将会在Class信息的static方法中寻找并执行main方法即入口方法,此时,JVM将会为入口方法开辟栈帧
进入main方法,可以看到我们的第一条语句是:
Student stu1 = new Student();
这里,我们将先新建引用stu1,由于是引用类型,默认赋值是null
然后代码向后执行,new关键词命令Jvm在堆中分配一个对象的空间
接着,栈帧插入方法Student(),由于栈的特征,先进后出,后进入的Student()方法先被执行完毕
其执行完毕后,出栈,之前分配的对象空间被赋上符合Student类的“特征”
class Student {
int age;
String name;
Computer computer;
public void play(){
System.out.printf("我在玩!");
}
public void studyByComputer(){
System.out.printf("我在用我家的"+computer.Brand+"牌的电脑学习!");
}
}
该类的特征为,有成员属性:name,age,computer,可以向她发送名为play()和studyByComputer()的消息
此时,语句
Student stu = new Student();
中的
Student stu;
和
new Student();
两句语句执行完毕,代码继续向后执行,将后者给前者复制,本质上,是把生成的对象的具体地址,交付给stu引用类型,此时,JVM内存更新为
stu1.name = "名字还没想好";
stu1.age = 24;
代码继续向后执行,此时,stu其实就是对象地址的代称,stu.name即是取出成员的地址,进行赋值
之后的代码,JVM本质执行和之前同样的行为
Computer com1 = new Computer();
com1.Brand = "联想";
stu1.computer = com1;
stu1.play();
stu1.studyByComputer();
后续的方法,相当于进入对象中找到他们并执行