我们的Java代码启动之后,是如何神奇地变成JVM进程的?

 2023-01-02
原文作者:石杉的架构笔记 原文地址:https://juejin.cn/post/7079967761270587422

目录

  • 知识点
  • 写好的Java代码,它是如何运行起来的
  • 它运行起来以后是如何变成JVM进程的
  • JVM进程又是如何运行我们写的代码的
  • 总结

知识点

今天给大家分享一个知识点,那就是平时我们 写好的Java代码,它是如何运行起来的以及它运行起来以后是如何变成JVM进程的?然后JVM进程又是如何运行我们写的代码的 ?这些问题想必很多写了很久Java的兄弟可能都不太清楚,今天我们就来好好聊聊这些问题。

写好的Java代码,它是如何运行起来的?

首先,咱们平时开发一个Java系统,其实说白了就是在自己本地的Intellij IDEA里写一大堆的Java代码,这些Java代码其实都是一些后缀为.java的文件,咱们写好的Java代码都是存放在这些.java后缀的文件里的, 本质跟我们用word写一个后缀为.doc的文档没啥区别 ,如下图。

202301011522087821.png

接着呢,假设你写好了一些代码,现在想把代码运行起来了,此时就应该要先做一个事情,那就是 编译 ,这个编译的话,意思就是说,你写好的Java代码,咱们的计算机是看不懂的,必须得把写好的代码编译成计算机能看懂的机器语言,就是字节码,所以这个时候我们就得先做编译这个动作,当然平时你要是用Intellij IDEA的话,其实它自动就给你把代码编译好了,编译好的字节码文件就是.class后缀的,如下图。

202301011522096022.png

接着呢,最关键的点来了,如果你是自己要运行代码,可以在命令行里对.class字节码文件发出java命令,如果你是用Intellij IDEA的,那么你点击运行代码按钮,人家自动就给你先编译代码,然后它自己直接用 java命令来运行.class字节码文件了 ,如下图。

202301011522101203.png

它运行起来以后是如何变成JVM进程的?

此时 java命令一旦发出,直接就会启动一个JVM进程 ,所以说,JVM进程就是这么来的,大家要注意,你任何Java类系统的运行都离不开JVM进程,当你用java命令运行你的代码的时候,就一定会启动一个JVM进程,接下来的事儿,就得交给这个JVM进程了,如下图。

202301011522106084.png

接着下一步,这个JVM进程必须要干一个事情,那就是 类加载 ,就是说它必须得把我们写好的类一各一个加载到内存里来,然后包括加载我们的**.class字节码指令**,接着才能运行我们写的代码,这个时候第一个要加载的类就是包含 main方法的主类 ,这个类就会第一个被加载到内存里来,同时这个类里的字节码指令也都是要开始被执行的,如下图。

202301011522111495.png

因此这个时候JVM有两个东西就可以引出来了, 一个是方法区 ,或者现在Java新版本也可以叫做元数据区,就是这个区域是存放我们加载到内存里的类的, 另外一个是程序计数器 ,就是这个东西是存放我们要运行的下一条字节码指令的,有人可能会说,这个字节码指令是什么?你大概可以认为, 字节码指令 就是我们的方法里写的代码,这些代码会转化为字节码指令,通过字节码指令运行我们的代码逻辑,如下图。

202301011522117156.png

给大家一点稍微形象点的例子,大家看下面一个包含main方法的类的代码:

    public class Application {     
        public static void main(String[] args) {  
            // 一大堆的代码  
        }   
     }

JVM进程又是如何运行我们写的代码的?

那所以当我们的JVM进程启动之后,针对这个Application类,是不是得加载出来扔自己的方法区这个内存区域里?然后接着JVM进程是不是得执行main方法里一行一行的代码?怎么执行代码?代码编译的时候都搞成 字节码指令了 ,这个时候就得通过 程序计数器 去引用一条一条的字节码指令,然后通过CPU去运行每条指令,就等于是在运行我们的每一行代码了,如下图。

202301011522124497.png

接着呢在字节码指令运行的过程中,可能会发现你的方法里会有一些局部变量,同时还会创建一些对象, 局部变量 会引用你创建出来的对象,具体代码可能类似于下面这样子,大家参考一下:

    public class Application {     
        public static void main(String[] args) { 
        User user = new User();  
            
        }  
    }

这个时候会发生什么事情呢?也很简单,对于那个User user,这是一个方法内的 局部变量 ,这种变量人家代码指令在运行期间,是会扔到一个地方叫做 栈内存 里去的,这个栈内存就是放你的方法代码运行期间的局部变量的,然后你用new User()创建的对象呢?这种对象是放到 堆内存 里去的,这个堆内存是专门放对象的,而且栈内存里的变量会指向堆内存里的对象,如下图。

202301011522129928.png

最后一个概念就是 线程 ,JVM进程实际上会默认开启一个main线程来运行main方法里的代码,也就是main方法里的字节码指令,另外你也可以在代码中开启别的线程的并发运行别的代码了,所以其实还应该引入一个线程执行代码的概念,如下图。

202301011522136919.png

总结

到此为止,我们就可以来总结一下咱们写好的Java代码是如何一步一步运行起来的,如何变成一个JVM进程在不断地运行的了,首先我们的Java代码得 编译成字节码 ,其次我们得用java命令 启动一个JVM进程 ,接着JVM进程会加载我们写好的 类到方法区里去 ,同时还会启动一个默认的 main线程 ,通过 程序计数器 去指向我们的main方法里的一条一条的指令,通过cpu来运行指令。

接着我们方法的代码指令运行期间,对于 局部变量都会扔栈内存里去 ,对于 创建的对象就会扔堆内存 里去,随着一个方法运行完毕,就会把这个方法的变量从栈内存里清理掉,然后方法运行期间创建的对象在堆内存里就没人引用了,此时过一段时间以后也会被JVM的一个垃圾回收线程给回收掉这些没人用的对象,这就是咱们的JVM运行原理了。

END

扫码 免费获取 600+页石杉老师原创精品文章汇总PDF

2023010115221430710.png

原创技术文章汇总

2023010115221481811.png