随着对java的进一步挖掘,深入到java虚拟机的时候,我才发现原来以前学的那些莫名其妙的方法终于有了合理的解释。
比如main()?我们俗称为主函数,作为程序初始线程的七点,同时将告知虚拟机为列在常量池第一项的类分配足够的内存。属于非守护线程。
守护线程通畅有虚拟机自己使用的线程,比如垃圾收集任务的线程。
下面我们用一个例子来分析一下,一个程序的运行,背后的虚拟机是怎么作用的。
class T{
private int speed =5;
void flow(){
}
}
class M{
public static void main(String[] args){
T t=new T();
t.flow();
}
}
按照之前的理解,这不就是很简单嘛,M类引用了T类然后调用T的方法。so easy!!
但是真是这样简单的吗?里面的引用是怎么引用的? M类怎么知道T类的存在的?难道M是先知吗?
事实是这样的:
运行M程序时,首相以某种“依赖于实现的”方式告诉虚拟机“M”这个名字,然后虚拟机会找到相对应的M.class文件并且将文件里面的二进制数据提取类型的信息放在方法区中。虚拟机开始执行main()方法,它会一直持有指向当前类(M类)的常量池(方法区中的一个数据结构)的指针。(注意:虚拟机执行M类的main()方法的字节码时,尽管T类没被装载,其实是使用懒加载模式。)main方法的第一条指令就是告知虚拟机为列在常量池第一项的类分配足够的内存。所以虚拟机使用指向M类常量池的指针找到第一项,发现它是一个对T类的符号引用(T t=new T();)。然后它就检查方法区看看T类有没有被装载。
当发现虚拟机还没有装载过名为“T”的类时,它就开始查找并装载文件“T.class”,并把从读入的二进制数据中提取的类型信息放在方法区中。接着,虚拟机以一个直接指向方法区的M类数据的指针替换常量池第一项(就是那个字符串“T”)-借助这个指针可以快速访问T类。这个替换过程叫做常量池解析,即把常量池中的符号引用替换成直接引用。
终于,虚拟机准备为新的T对象分配内存,虚拟机借助在常量池第一项的指针访问到T类型信息,这样可以确定T对象需要分配多少堆空间。当虚拟机确定T对象的大小后,它就在对上分配这么大的空间,并且把这个对象实例speed初始化为默认初始值0,当把新生的T对象的引用压到栈中,main()方法的第一条指令也完成了。。。坑爹啊!!接下来通过这个引用调用java代码(将代码中的speed变量初始化为正确初始值5),另外一条指令将用这个引用调用T对象的flow()方法。
---------------------------------------------整个过程,相对简略一点,如果想详细了解,请看深入java虚拟机---------------------------------------