本人最近学习一些有关jvm东西,谈谈对Java编译 和 运行时的一点拙见
一 编译
目的时生成字节码文件,class文件
生成方法
1 刚学java时,javac xx.java
2 编辑器自动帮我们编辑
编译后的字节码文件格式主要分为两部分:常量池和方法字节码。常量池记录的是代码出现过的所有token(类名,成员变量名等等)以及符号引用(方法引用,成员变量引用等等);方法字节码放的是类中各个方法的字节码。
二 运行
这一块内容很多:大致有两个过程:1、类的加载 2、类的执行。
1 讲到类加载过程,比较经典的回答:
一般来说,我们把 Java 的类加载过程分为三个主要步骤:加载、链接、初始化,具体行为在
首先是加载阶段(Loading),它是 Java 将字节码数据从不同的数据源读取到 JVM 中,并映射为 JVM 认可的数据结构(Class 对象),这里的数据源可能是各种各样的形态,
如 jar 文件、class 文件,甚至是网络数据源等;如果输入数据不是 ClassFile 的结构,则会抛出 ClassFormatError。
第二阶段是链接(Linking),这是核心的步骤,简单说是把原始的类定义信息平滑地转化入 JVM 运行的过程中。这里可进一步细分为三个步骤:
验证(Verification),这是虚拟机安全的重要保障,JVM 需要核验字节信息是符合 Java 虚拟机规范的,否则就被认为是 VerifyError,这样就防止了恶意信息或者不合规的信息危害 JVM 的运行,验证阶段有可能触发更多 class 的加载。
准备(Preparation),创建类或接口中的静态变量,并初始化静态变量的初始值。但这里的“初始化”和下面的显式初始化阶段是有区别的,侧重点在于分配所需要的内存空间,不会去执行更进一步的 JVM 指令。
解析(Resolution),在这一步会将常量池中的符号引用(symbolic reference)替换为直接引用。
最后是初始化阶段(initialization),这一步真正去执行类初始化的代码逻辑,包括静态字段赋值的动作,以及执行类定义中的静态初始化块内的逻辑,
编译器在编译阶段就会把这部分逻辑整理好,父类型的初始化逻辑优先于当前类型的逻辑。
2 类的执行
public class MainApp {
public static void main(String[] args) {
Animal animal = new Animal("didid");
animal.printName();
}
}
public class Animal {
public String name;
public Animal(String name) {
this.name = name;
}
public void printName() {
System.out.println("Animal:"+name);
}
}
- 第一个执行逻辑:Animal animal = new Animal("didid");就是让JVM创建一个Animal对象,但是这时候方法区中没有Animal类的信息,所以JVM马上加载Animal类,把Animal类的类型信息放到方法区中。
- 加载完Animal类之后,Java虚拟机做的第一件事情就是在堆区中为一个新的Animal实例分配内存, 然后调用构造函数初始化Animal实例,这个Animal实例持有着指向方法区的Animal类的类型信息(其中包含有方法表,java动态绑定的底层实现)的引用。
- 当使用animal.printName()的时候,JVM根据animal引用找到Animal对象,然后根据Animal对象持有的引用定位到方法区中Animal类的类型信息的方法表,获得printName()函数的字节码的地址。
- 开始运行printName()函数。
三 总结
算是个人的一个总结,集大家所长,供大家参考,有不对的地方请指教!
参考:https://www.cnblogs.com/qiumingcheng/p/5398610.html