什么是java虚拟机
当谈到java虚拟机时一般会涉及到下面三个方面:- 抽象规范
- 具体实现
- 运行实例
每个java程序运行在一个抽象的虚拟机规范的某种具体实现的运行实例上。
java虚拟机的生命周期
当以个程序启动时,一个java虚拟机运行实例被创建,当程序运行结束时,虚拟机运行实例被销毁,每个java程序对应一个java虚拟机。java虚拟机通过调用类的main()方法启动应用程序,main()方法必须是public,static,返回void,并且接收一个String数组参数,任何具有一个main()方法的类都可以作为程序的入口。下面是一个例子:
class Echo {
public static void main(String[] args) {
int len = args.length;
for (int i = 0; i < len; ++i) {
System.out.print(args[i] + " ");
}
System.out.println();
}
}
如果要运行Echo程序,需要在输入下面的指令:
java Echo Greetings, Planet
“java”表示操作系统需要启动的java虚拟机。“Echo”是初始类的名字。初始类必须有一个public static,返回值为void,且可接受一个String数组参数的mian()方法。“Greetings, Planet”是应用程序的命令行参数。它们会按顺序传给main()方法的String数组参数。
在java虚拟机内部有两种线程:daemon线程(守护线程)和non-daemon线程。daemon线程是虚拟机自身使用的线程,例如一个执行垃圾回收的线程。应用程序内部创建的线程默认为non-daemon线程,应用程序也可以通过标记创建一个daemon线程。
只要有一个non-daemon线程在运行,虚拟机保持存活状态,当程序的所有non-daemon线程终止时,虚拟机实例就会退出。
java虚拟机的架构
在java虚拟机规范中,用子系统,内存区域,数据类型,指令集描述了一个java虚拟机实例的行为,这些组件描述了虚拟机的抽象内部架构。下图是java虚拟机的架构,每个java虚拟机有一个类加载器子系统:根据给定的全限定名来加载类型(classes和interfaces)的机制。每个java虚拟机还具有一个执行引擎:负责执行加载classes的方法内部的指令。
当java虚拟机运行程序时,它需要存储很多东西到内存,包括字节码和它从加载的class文件中获取的其他信息,对象实例,方法参数,返回值,本地变量和中间计算结果。java虚拟机将内存组织成几个运行时区域来执行一个程序。
每个java虚拟机实例有一个
方法区和一个
堆,这两个区域是被虚拟机中所有运行线程共享的。当虚拟机加载一个class文件时,会从class文件的二进制数据中解析出类型信息,虚拟机将这些类型信息放到方法区中。随着程序的运行,虚拟机会把程序实例化的所有对象放到堆中。如下图所示:
每个新创建的线程都有一个属于自己的
程序计数器(pc register)和
java栈。如果线程在执行一个java方法,程序计数器的值指向下一条执行指令。一个线程的java栈存储了该线程的java方法调用状态,这个java方法调用状态包括它的本地变量,调用参数,返回值和中间计算结果。native方法调用的状态存储在native方法栈上,也可能在寄存器或依赖于实现的存储区域中。
java虚拟机中没有可以存储中间数据值的寄存器,指令集使用java栈来存储中间数据值。下图描述了java虚拟机为每个线程创建的内存区域,这些区域是线程私有的。
由上图所示,线程一和线程二在执行java方法,线程三在执行native方法。当前执行方法的栈帧用淡颜色表示,执行java方法线程的程序计数器指向下一条执行指令,这样的程序计数器在图中用淡颜色表示,线程三当前执行的是native方法,它的程序计数器的值为undefine,图中用深灰色表示这种程序计数器。