作为一个接触过一段时间java的人来说,之前很少去思考这里面到底是怎么在执行的,在后面想要去了解这里面深层的机制和原理的时候就头疼了许久,所以,把一些从网上整理过来的资料以及一些我自己的理解都记录下来,作为自己的学习记录。
1.把握全局
Java程序从源文件创建到程序运行要经过两大步骤:
1、源文件.java由编译器编译成字节码.class
2、字节码由java虚拟机解释运行。
2.例子说明
在百度知道上看了一个不错的回答,写的很清楚,所以就把它拿过来了。
1)源文件.java由编译器编译成字节码.class
新建两个类Student和Test,Test打印学生对象的值
java在编译Test的时候,这个时候如果它依赖的学生类还没有被编译的话,就会先编译它依赖的这个Student类,然后再编译Test.
编译后的字节码文件格式主要分为两部分:常量池和方法字节码。
常量池记录的是代码出现过的所有token(类名,成员变量名等等)以及符号引用(方法引用,成员变量引用等等);
方法字节码放的是类中各个方法的字节码。
2)字节码由java虚拟机解释运行
java类运行的时候大概可以分为两个过程:类的加载和类的执行。
类的加载:JVM都是在程序不得不用的时候再来加载这些类,而非加载该程序下所有的类,而且只加载一次就够了。
继续上面的例子,编译之后得到了Test.class,
用java来运行的时候,系统就会启动一个jvm进程,jvm进程从classpath路径中找到一个名为Test.class的二进制文件,将Test的类信息加载到运行时数据区的方法区内,这个过程叫做Test类的加载。
然后JVM找到Test的主函数入口,开始执行main函数。
main函数的第一条命令是Student stu= new Student(“helen”);
就是让JVM创建一个Student对象,但是这时候方法区中没有Student类的信息,所以JVM马上加载Student类,把Student类的类型信息放到方法区中。
加载完Student类之后,Java虚拟机做的第一件事情就是在堆区中为一个新的Student实例分配内存, 然后调用构造函数初始化Student实例,这个Student实例持有着指向方法区的Student类的类型信息(其中包含有方法表,java动态绑定的底层实现)的引用。
当使用stu.getName()的时候,JVM根据stu引用找到Student对象,然后根据Student对象持有的引用定位到方法区中Student类的类型信息的方法表,获得getName()函数的字节码的地址。
开始运行getName()函数。
特别说明:java类中所有public和protected的实例方法都采用动态绑定机制,所有私有方法、静态方法、构造器及初始化方法都是采用静态绑定机制。而使用动态绑定机制的时候会用到方法表,静态绑定时并不会用到。