知识点
今天给大家分享一个知识点,那就是平时我们写好的 Java 代码,他是如何运行起来的,以及他运行起来以后是如何变成 JVM 进程的?然后 JVM 进程又是如何运行我们写的代码的?
这些问题想必很多写了很久 Java 的兄弟可能都不太清楚,今天我们就来好好聊聊这些问题。
写好的 Java 代码,他是如何运行起来的?
首先,咱们平时开发一个 Java 系统,其实说白了就是在自己本地的 Intellij IDEA 里写一大堆的 Java 代码。
这些 Java 代码其实都是一些后缀为 .java 的文件,咱们写好的 Java 代码都是存放在这些 .java 后缀的文件里的,本质跟我们用 word 写一个后缀为 .doc 的文档没啥区别。
如下图:
接着呢,假设你已经写好了一些代码,现在想把代码运行起来了,此时就应该要先做一个事情,那就是编译。
这个编译的话,意思就是说,你写好的 Java 代码,咱们的计算机是看不懂的,必须得把写好的代码编译成计算机能看懂的机器语言,就是字节码,所以这个时候我们就得先做编译这个动作。
当然平时你要是用 Intellij IDEA 的话,其实他自动就给你把代码编译好了,编译好的字节码文件就是 .class 后缀的。
如下图:
接着呢,最关键的点来了,如果你是自己要运行代码,可以在命令行里对 .class 字节码文件发出 java 命令,如果你是用 Intellij IDEA 的,那么你点击运行代码按钮,人家自动就给你先编译代码,然后他自己直接用 java 命令来运行 .class 字节码文件了。
如下图:
他运行起来以后是如何变成 JVM 进程的?
此时 java 命令一旦发出,直接就会启动一个 JVM 进程,所以说,JVM 进程就是这么来的。
大家要注意,你任何 Java 类系统的运行都离不开 JVM 进程,当你用 java 命令运行你的代码的时候,就一定会启动一个 JVM 进程,接下来的事儿,就得交给这个 JVM 进程了。
如下图:
接着下一步,这个 JVM 进程必须要干一个事情,那就是类加载,就是说他必须得把我们写好的类一各一个加载到内存里来,然后包括加载我们的 .class 字节码指令,接着才能运行我们写的代码。
这个时候第一个要加载的类就是包含 main 方法的主类,这个类就会第一个被加载到内存里来,同时这个类里的字节码指令也都是要开始被执行的。
如下图:
因此这个时候 JVM 有两个东西就可以引出来了,一个是方法区,或者现在 Java 新版本也可以叫做元数据区,就是这个区域是存放我们加载到内存里的类的。
另外一个是程序计数器,就是这个东西是存放我们要运行的下一条字节码指令的,有人可能会说,这个字节码指令是什么?
你大概可以认为,字节码指令就是我们的方法里写的代码,这些代码会转化为字节码指令,通过字节码指令运行我们的代码逻辑。
如下图:
给大家一点稍微形象点的例子,大家看下面一个包含 main 方法的类的代码:
public class Application {
public static void main(String[] args) {
// 一大堆的代码
}
}
JVM 进程又是如何运行我们写的代码的?
那所以当我们的 JVM 进程启动之后,针对这个 Application 类,是不是得加载出来扔进自己的方法区这个内存区域里?然后接着 JVM 进程是不是得执行 main 方法里一行一行的代码?怎么执行代码?
代码编译的时候都搞成字节码指令了,这个时候就得通过程序计数器去引用一条一条的字节码指令,然后通过 CPU 去运行每条指令,就等于是在运行我们的每一行代码了。
如下图:
接着呢在字节码指令运行的过程中,可能会发现你的方法里会有一些局部变量,同时还会创建一些对象,局部变量会引用你创建出来的对象。
具体代码可能类似于下面这样子,大家参考一下:
public class Application {
public static void main(String[] args) {
User user = new User();
}
}
这个时候会发生什么事情呢?也很简单,对于那个 User user,这是一个方法内的局部变量,这种变量人家代码指令在运行期间,是会扔到一个地方叫做栈内存里去的。
这个栈内存就是放你的方法代码运行期间的局部变量的,然后你用 new User() 创建的对象呢?
这种对象是放到堆内存里去的,这个堆内存是专门放对象的,而且栈内存里的变量会指向堆内存里的对象。
如下图:
最后一个概念就是线程,JVM 进程实际上会默认开启一个 main 线程来运行 main 方法里的代码,也就是 main 方法里的字节码指令。
另外你也可以在代码中开启别的线程的并发运行别的代码了,所以其实还应该引入一个线程执行代码的概念。
如下图:
总结
到此为止,我们就可以来总结一下咱们写好的 Java 代码是如何一步一步运行起来的,如何变成一个 JVM 进程在不断地运行的了。
首先我们的 Java 代码得编译成字节码,其次我们得用 java 命令启动一个 JVM 进程,接着 JVM 进程会加载我们写好的类到方法区里去,同时还会启动一个默认的 main 线程,通过程序计数器去指向我们的 main 方法里的一条一条的指令,通过 CPU 来运行指令。
接着我们方法的代码指令运行期间,对于局部变量都会扔栈内存里去,对于创建的对象就会扔堆内存里去。
随着一个方法运行完毕,就会把这个方法的变量从栈内存里清理掉,然后方法运行期间创建的对象在堆内存里就没人引用了,此时过一段时间以后也会被 JVM 的一个垃圾回收线程给回收掉这些没人用的对象,这就是咱们的 JVM 运行原理了。