标签:
虚拟机位于机器和编译程序之间,这是一层抽象的虚拟的机器。虚拟机在任何平台上都提供给编译程序一个共同的接口,编译程序面向虚拟机,生成虚拟机能够理解的代码,然后解释器将虚拟机代码(java中就是字节码,class文件,只面向虚拟机)转换为特定系统的机器码执行。每个平台的解释器是不一样的,但是实现的虚拟机是相同的。可以说,解释器是虚拟机的一部分,一台机器,只要配备了解释器,就可以运行class字节码,不管这个class字节码是在什么平台上编译生成的。Java源程序经过编译器编译后变成字节码,字节码由虚拟机解释执行,虚拟机将每一条要执行的字节码送给解释器,解释器将其翻译成特定机器上的机器码,然后在特定的机器上运行。
编译 解释器
java源文件------->字节码(class文件)--------->机器码
JVM(Java Virtual Machine)运行过程:
现在存在两个java源文件:
package demo.jvm; public class User { private String name; private String pwd; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } }
package demo.jvm; public class Main { private static int size=1; public static void main(String args[]) { User u = new User(); u.setName("tom"); u.setPwd("123"); String name = u.getName(); String pwd = u.getPwd(); u = null; } }
首先将两个源文件编译为class字节码,然后,jvm会执行:
1,装载
java虚拟机使用类装载器定位到相应的CLASS文件,然后读取这个CLASS文件(一个线性二进制数据流),将它传入java虚拟机中。紧接着虚拟机提取其中的类型信息。比如:该类的类名,方法名,变量名,修饰符,方法的返回类型等等。还有一个重要的东西就是常量池。(常量池保存了该类型的所有常量,包括直接常量和对其他类型,字段,方法的符号引用)将这些信息保存在一个叫做方法区的地方。最终形成CLASS类的实例,这个实例存放在内存的堆区。它成为了java程序与内部数据结构之间的接口,程序要访问该类型的信息,程序就调用该类型对应的CLASS实例对象的方法。简而言之:这个过程就是把一个类型的二进制数据解析为方法区中的内部数据结构,并在堆上建立一个CLASS对象的过程。例如,装载Main类:Java虚拟机读取Main类的CLASS文件,生产对应的java.lang.Class类的实例,读取其中的类型信息,比如修饰符 private,public,static,另外变量 size,name,pwd,User(User即为一个引用)共同构成了这个类的常量池。将这些信息保存在方法区。
2,链接
分为三步:校验、准备、解析。
1)校验:确定类型符合java语言的语义,比如:final类不能有子类,final方法不能被覆盖,确保在类型和超类型之间没有不兼容的方法声明(比如两个方法拥有同样的名字,参数完全相同,但返回类型不同)。2)准备:java虚拟机为类变量分配内存,设置默认值。3)解析:在类型的常量池中寻找类,接口,字段和方法的符合引用把这些符号引用替换成直接引用的过程。例如,链接Main类:Java虚拟机为size分配内存,并赋默认值0.找到常量池中User类的引用,如果User类还没有被装载,则装载并且连接该类,然后将常量池中对User类的引用替换为直接引用。在此时User类并不会被初始化,因为还没有用它。
3,初始化
初始化一些静态变量
过程:可能会调用()方法,(这个方法只能够由java虚拟机调用)来初始化该类的静态变量。在调用这个方法前,必须确认该类的超类的() 方法已经被调用。例如,初始化Main类:Java虚拟机将Main类的静态变量赋值为1.
4,使用(执行该类代码)
1.User u = new User();(存放在内存的堆区)
创建了一个User类实例,实际上是通过这个类的CLASS实例实例化的。方法如下:
User u=(User)Class.forName("User").newInstance();
为了方便,用C代替Class.forName("User")
2.u.setName("tom"); u.setPwd("123");
调用该类的方法,为该类的变量赋值,Java虚拟机内部调用是这样的,通过方法区找到该方法,利用CLASS实例的如下方法调用:
c.getMethod("setName").invoke(u,"tom");
3.String name = u.getName();String pwd = u.getPwd();
与第二步类似,不同的是将取得的值分别赋给了变量name和pwd。关键是这个值保存在哪里?和实例对象一样,存放在堆区。这个时候我应该可以看出CLASS实例的作用了,它就是起个中间作用,将程序中的调用反应到堆区上数据的变化。
4.u = null;
这个步骤写出来的目的是了解一下Java虚拟机垃圾回收机制。Java虚拟机内部会根据一种规则(这个对象是否可以触及)来判断这两个类是否可以回收了。具体形式如下:
当执行 u = null;时这条线就被斩断了,因此User实例就不可以触及了,所以java虚拟机就可以回收这个User实例了
原文:http://www.cnblogs.com/o-andy-o/archive/2012/04/11/2442109.html
标签:
原文地址:http://www.cnblogs.com/sunyt/p/4326060.html