昨天面试的时候被面试官问了一道这个问题, 答的磕磕绊绊, 感觉自己对于JVM的运行过程的了解不够清晰, 现在记录一下, 试着用自己的语言来清晰的说明一下整个运行过程. 哎, 感觉自己好菜...
在理解JVM的运行过程之前, 我们首先要知道JVM的体系结构是什么样子的, 如下图:
-
运行时数据区 当java文件通过编译之后, 生成的class字节码文件会由Class Loader类加载器进行加载, 然后交给执行引擎去执行, 在执行的过程中会产生一些数据, 这些数据会被存储到一块内存区域, 这就是运行时数据区.
-
程序计数器 用于记录当前线程正在执行的字节码指令的位置. 由于虚拟机是多线程的, 我们需要记录下来不同线程的执行位置以便于切换线程和分配CPU, 所以程序计数器是线程私有的.
-
虚拟机栈 每个java方法执行的时候会创建一个栈帧, 用于存储局部变量等信息. 当方法执行完毕之后, 从栈弹出.
-
本地方法栈 和虚拟机栈类似, 本地方法的执行会用到本地方法栈.
5.方法区 用于存储已被虚拟机加载的类信息, 常量以及静态变量等数据.
6.堆 主要用来存放程序运行过程中创建的实例对象. 大部分的垃圾回收是发生在这里的.
现在了解了JVM的这些内存结构之后, 我们来看执行Hello World的代码的时候到底发生了什么.
假设我们的代码如下所示:
// HelloWorld.java
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
然后我们通过IDE或者使用命令javac HelloWorld.java进行编译, 得到HelloWorld.class文件.
接下来当我们通过IDE或者java HelloWorld去执行这个Class文件的时候, 字节码文件(Class文件)通过类加载器(Class Loader)加载完毕并交付给执行引擎去执行. 这个时候类加载机制会把HelloWorld中的常量, 静态变量, 类的信息加载到方法区. 如果我们需要创建这个类的实例的话, 需要使用new关键字和后面的参数去方法区找到类的相关信息.
当类加载完毕, JVM会检查程序的入口, 并根据public static关键字去找到跟主类关联的main方法, 在这个例子中, 虚拟机会去找到HelloWolrd.class中的main函数, 并生成一个栈帧入栈到虚拟机栈中. 如果执行的过程中有用到本地方法, 则会在本地方法栈中出入栈.
补充:
- main函数中的参数
String[] args在JVM中的运行过程: 系统首先加载String[].class字节码文件到方法区, 然后为变量args(存放的是String[]的地址)在主线程的虚拟机栈中创建一个新的栈帧并指向堆中存放的数据(数据是String[]).