参考链接:https://blog.csdn.net/kuangay/article/details/81485324
首先,.java(.java)文件经过javac编译,成为字节码(.class)文件,后通过jvm经过操作系统联系硬件。
JVM的组成
jvm由三部分组成:类装载子系统(加载.class文件),运行时数据区,执行引擎(进行垃圾回收操作【GC】)。其中重点介绍运行时数据区。运行时数据区分为五个部分:堆区,方法区,栈区,本地方法栈,程序计数器(其中堆区与方法区所有线程共享,栈区,本地方法栈,程序计数器每个线程之间独立开辟)
- 栈区:存储线程在运行期间产生的局部变量,每个线程分配一块栈空间。一个栈区含有三部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。每个栈帧含有四个部分:局部变量表,操作数栈,动态链接,方法出口,动态链接。
栈帧:每一个方法的范围为一个栈帧
局部变量表: 存储的为局部变量名
操作数栈:存储的是局部变量值(用于对局部变量表中的变量进行赋值)
方法出口:存储的是返回值与当前程序所在的行号
动态链接:程序访问方法时,会通过符号引用来找到源代码。即通过符号引用来使方法名与方法代码进行联系 - 方法区:存储的使静态变量,常量,类元信息(程序代码,类信息【父类,接口…】),并且直接存储到电脑上(不再jvm内存中)
- 堆区:存储new开辟出的空间(引用数据类型)。分为伊甸区,from,to,老年区四部分。
执行步骤:new开辟出的对象首先存储到伊甸区中,当伊甸区满后,有执行引擎执行GC操作。将需要处理的对象存到from中,带from满后,存储到to中,等到to满后重新存到from中。当一个对象被GC手机15次后,对象存储到老年区中,带老年区满后,进行fullGC操作。 - 本地方法栈:线程私有,运行java的native方法,后进先出。
- 程序计数器:记录档期那进程所执行的字节码的行号
JAVA运行
eg1:
package lianxi;
class Person{
static{
System.out.println("person static");
}
public Person(String str) {
System.out.println("person "+str);
}
}
class MyClass extends P {
Person person = new Person("MyClass");
static{
System.out.println("myclass static");
}
public MyClass() {
System.out.println("myclass constructor");
}
}
class P {
Person person = new Person("Test");
static{
System.out.println("test static");
}
public P() {
System.out.println("test constructor");
}
public static void main(String[] args) {
new MyClass();
}
}
运行结果:
test static
myclass static
person static
person Test
test constructor
person MyClass
myclass constructor
分析:首先P.java由javac进行编译成字节码文件(.class)文件,随后在P类中寻找main方法,(若P类中无main方法,则程序会报错,main方法必须在P类中),所以应首先加载P类,在加载P类时,应先执行static方法段。接着main方法开始运行,执行第一行语句,加载MyClass类,若加载MyClass应首先加载父类P,而P类已经加载,加载完MyClass后,实例化MyClass类后,应先加载父类(P类的成员变量)进而加载并实例化Person类,在进行P类,与MyClass的实例化。
若想深入了解static关键字,请参考: https://mp.csdn.net/mdeditor/102839816#
eg2:
package lianxi;
public class P {
public static void main(String args[]) {
QQ q=new QQQ();
q.setName("张三");
q.info(); //当q为Q q=new QQQ()时,则不能调用info()方法。
}
}
abstract class Q{
String name="nihao";
public abstract void setName(String name);
public String getName() {
return name;
}
}
abstract class QQ extends Q{
public abstract void info();
}
class QQQ extends QQ{
@Override
public void info() {
System.out.println("姓名为:"+name);
}
@Override
public void setName(String name) {
this.name=name;
}
}
分析:首先找到mian方法,在运行数据去为该线程建立一个栈区,实例化QQQ,加载QQQ父类类及本类,实例化QQQ类,并在堆中开辟空间存储QQQ对象(只存储属性),接着根据动态链接找到setName方法,加载并执行(在执行的时候开辟出一个栈帧存储本方法中的局部变量),修改name之后,栈帧被弹出。同理调用info方法。
若有不足,请多多指点。