java一个方法调用的虚拟机实现


先随便写一个非常简单的类Test

public class Test{
	private int i;
	public void testMethod(){
		i=i+1;
	}
}

然后被下面这个main方法生成并调用,期间我们通过期间发生的内容,来简单了解一下JVM的内部机制。

public static void main(String[] args) {
		Test test =  new Test();
		test.testMethod();
	}

首先是编译:

Test类被编译成一个叫做Test.class的类文件,它的内容以字节码的形式存在。即Ox0F这样的形式存在。

这个类文件中依次包含了魔数、常量池、类索引、父类索引、接口索引集合、字段表集合、方法表集合、属性表集合。

这里我们来回答几个问题:

1、对一个方法进行调用,入口地址在哪:常量池中的方法常量,在编译期间记录的是符号引用,里面的nameAndType唯一确定了这个方法对应类中的哪一个,特征为参数和方法名,不指向代码,在编译期间,虚拟机通过这种方式已经解决了方法重载的问题,关于如何实现覆盖(override)是运行时实现的。

2、一个方法的属性、参数类型、返回值类型、名称等在哪:方法表集合中记录了所有属于这个类的方法(不包括继承来的),常量池中的方法常量中也有一个NameAndType的引用。

3、一个方法的Code在哪:属性表集合中有一个叫做Code属性的字段,它的内容是Code表集合,里面有属于这个类的各个方法Code(不包括继承来的方法)。


所以一个方法在一个类中的存在,被三个地方记录,一个是入口,一个是属性,一个是code。

在完成编译之后,几样东西已经确定:即main方法的code中,调用方法的指令指向了Test类的常量池中的testMethod方法,已经确定了调用的符号引用。


然后是类加载期间:

加载过程被分为了5个阶段,加载、校验、准备、解析、初始化。

其中,加载通过classLoader把类加载进了内存中的方法区。方法区包括了类信息和运行时常量区两个部分,其中class的常量池被加载入运行时常量区,其他信息加载入类信息处。到这里,我们跟踪一下:

1、调用入口在运行时常量池,内容还是符号引用,。

2、方法描述在方法区的类信息区。

3、code在方法区的属性表集合的code属性的code属性表中。

此时,与上一步没有本质的变化。


然后是校验、准备、解析。每一步的功能可以查阅其他资料。

这里解析这一步比较关键,它将常量池中的符号引用替换为了真实的内存引用。但是注意了,这里并没有把所有的方法的符号引用都替换掉。这里被替换掉的符号引用有:

静态方法、私有方法、实例构造器方法、父类方法四类。他们有个共同特点:唯一确定,没有override。

所以这里的testMethod没有被解析,还是一个符号引用。

而如果有构造方法,那么在这里已经被解析了。

这样做实现了重载。因为方法到这一步的时候,并不知道他的实际调用对象是谁,只知道他的静态类型是Test,可能是Test,也可能是test的子类,如果是test的子类,事实上可能指向子类重载的方法,也可能还是指向父类的方法(子类没有重载)。这就涉及到运行是的动态分派了。

最后初始化,这时没有发生太大的根方法有关的变化。

针对testMethod,继续跟踪一下,发现没有变化。

但如果假设他是静态方法,那么会是这样:

1、入口在运行是常量池,内容是直接引用,指向code所在的内存(这里可能不准确,实际可能还要带上方法信息)。

2、其他内容位置不变。


最后是运行。

当运行到方法被调用的时候,这个时候就需要获得这个类调用对象的信息了:


分析这里的test,得到他的类型为Test。同时根据调用信息,得到符号引用。

根据符号引用在Test的方法表集合中搜索符合符号引用的方法,如果找到了,则将调用地址指向Test中的testMethod的code。否则依次在父类中查找,找到则指向那个方法。如果查找失败,则抛异常。通过这个方式实现了多态性。

随后根据动态指向的code中的信息,生成栈帧。这里需要根据code得到max_locals、max_stacks数据,这些数据是在编译时计算好放在code属性中的。

到这里,就完成了一次方法的调用。由于涉及到jvm的知识点较多,阅读前需要先做了解,如有错误,请指正。





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值