咱们都知道java程序必须在虚拟机上运行。那么虚拟机究竟是什么呢?先看网上搜索到的比较靠谱的解释:html
虚拟机是一种抽象化的计算机,经过在实际的计算机上仿真模拟各类计算机功能来实现的。Java虚拟机有本身完善的硬体架构,如处理器、堆栈、寄存器等,还具备相应的指令系统。JVM屏蔽了与具体操做系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就能够在多种平台上不加修改地运行。java
这种解释应该算是正确的,可是只描述了虚拟机的外部行为和功能,并无针对内部原理作出说明。通常状况下咱们不须要知道虚拟机的运行原理,只要专一写java代码就能够了,这也正是虚拟机之因此存在的缘由--屏蔽底层操做系统平台的不一样而且减小基于原生语言开发的复杂性,使java这门语言可以跨各类平台(只要虚拟机厂商在特定平台上实现了虚拟机),而且简单易用。这些都是虚拟机的外部特性,可是从这些信息来解释虚拟机,未免太笼统了,没法让咱们知道内部原理。linux
让咱们尝试从操做系统的层面来理解虚拟机。咱们知道,虚拟机是运行在操做系统之中的,那么什么东西才能在操做系统中运行呢?固然是进程,由于进程是操做系统中的执行单位。能够这样理解,当它在运行的时候,它就是一个操做系统中的进程实例,当它没有在运行时(做为可执行文件存放于文件系统中),能够把它叫作程序。程序员
对命令行比较熟悉的同窗,都知道其实一个命令对应一个可执行的二进制文件,当敲下这个命令而且回车后,就会建立一个进程,加载对应的可执行文件到进程的地址空间中,而且执行其中的指令。下面对比C语言和Java语言的HelloWorld程序来讲明问题。编程
首先编写C语言版的HelloWorld程序。架构
编译C语言版的HelloWorld程序:编程语言
编译成了系统可识别的语言,可运行的命令学习
运行C语言版的HelloWorld程序:spa
gcc编译器编译后的文件直接就是可被操做系统识别的二进制可执行文件,当咱们在命令行中敲下 ./HelloWorld这条命令的时候, 直接建立一个进程, 而且将可执行文件加载到进程的地址空间中, 执行文件中的指令。操作系统
做为对比, 咱们看一下Java版HelloWord程序的编译和执行形式。
首先编写源文件HelloWord.java :
编译Java版的HelloWorld程序:
能够看到是编译成了.class文件
运行Java版的HelloWorld程序:
从上面的过程能够看到, 咱们在运行Java版的HelloWorld程序的时候, 敲入的命令并非 ./HelloWorld.class 。 由于class文件并非能够直接被操做系统识别的二进制可执行文件 。 咱们敲入的是java这个命令。 这个命令说明, 咱们首先启动的是一个叫作java的程序, 这个java程序在运行起来以后就是一个JVM进程实例。
上面的命令执行流程是这样的:
java命令首先启动虚拟机进程,虚拟机进程成功启动后,读取参数“HelloWorld”,把他做为初始类加载到内存,对这个类进行初始化和动态连接(关于类的初始化和动态连接会在后面的博客中介绍),而后从这个类的main方法开始执行。也就是说咱们的.class文件不是直接被系统加载后直接在cpu上执行的,而是被一个叫作虚拟机的进程托管的。首先必须虚拟机进程启动就绪,而后由虚拟机中的类加载器加载必要的class文件,包括jdk中的基础类(如String和Object等),而后由虚拟机进程解释class字节码指令,把这些字节码指令翻译成本机cpu可以识别的指令,才能在cpu上运行。
从这个层面上来看,在执行一个所谓的java程序的时候,真真正正在执行的是一个叫作Java虚拟机的进程,而不是咱们写的一个个的class文件。这个叫作虚拟机的进程处理一些底层的操做,好比内存的分配和释放等等。咱们编写的class文件只是虚拟机进程执行时须要的“原料”。这些“原料”在运行时被加载到虚拟机中,被虚拟机解释执行,以控制虚拟机实现咱们java代码中所定义的一些相对高层的操做,好比建立一个文件等,能够将class文件中的信息看作对虚拟机的控制信息,也就是一种虚拟指令。
编程语言也有本身的原理, 学习一门语言, 主要是把它的原理搞明白。 看似一个简单的HelloWorld程序, 也有不少深刻的内容值得剖析。
为了展现虚拟机进程和class文件的关系,特地画了下面一张图:
根据上图表达的内容,咱们编译以后的class文件是做为Java虚拟机的原料被输入到Java虚拟机的内部的,那么具体由谁来作这一部分工做呢?其实在Java虚拟机内部,有一个叫作类加载器的子系统,这个子系统用来在运行时根据须要加载类。注意上面一句话中的“根据须要”四个字。在Java虚拟机执行过程当中,只有他须要一个类的时候,才会调用类加载器来加载这个类,并不会在开始运行时加载全部的类。就像一我的,只有饿的时候才去吃饭,而不是一次把一年的饭都吃到肚子里。通常来讲,虚拟机加载类的时机,在第一次使用一个新的类的时候。本专栏后面的文章会具体讨论Java中的类加载器。
由虚拟机加载的类,被加载到Java虚拟机内存中以后,虚拟机会读取并执行它里面存在的字节码指令。虚拟机中执行字节码指令的部分叫作执行引擎。就像一我的,不是把饭吃下去就完事了,还要进行消化,执行引擎就至关于人的肠胃系统。在执行的过程当中还会把各个class文件动态的链接起来。关于执行引擎的具体行为和动态连接相关的内容也会在本专栏后续的文章中进行讨论。
咱们知道,Java虚拟机会进行自动内存管理。具体说来就是自动释放没有用的对象,而不须要程序员编写代码来释放分配的内存。这部分工做由垃圾收集子系统负责。
从上面的论述能够知道, 一个Java虚拟机实例在运行过程当中有三个子系统来保障它的正常运行,分别是类加载器子系统, 执行引擎子系统和垃圾收集子系统。 以下图所示:
虚拟机的运行,必须加载class文件,而且执行class文件中的字节码指令。它作这么多事情,必须须要本身的空间。就像人吃下去的东西首先要放在胃中。虚拟机也须要空间来存放个中数据。首先,加载的字节码,须要一个单独的内存空间来存放;一个线程的执行,也须要内存空间来维护方法的调用关系,存放方法中的数据和中间计算结果;在执行的过程当中,没法避免的要建立对象,建立的对象须要一个专门的内存空间来存放。关于虚拟机运行时数据区的内容,也会出如今本专栏后续的文章中。虚拟机的运行时内存区大概能够分红下图所示的几个部分。(这里只是大概划分, 并无划分的很精细)
写到这里,基本上关于我对java虚拟机的理解就写完了。这篇文章的主题虽然是深刻理解Java虚拟机,可是你可能感受一点也不“深刻”,也只是泛泛而谈。我也有这样的感受。限于本身水平有限,也只能这样了,要是想深刻理解java虚拟机,强烈建议读一下三本书:
《深刻Java虚拟机》
《深刻理解Java虚拟机JVM高级特性与最佳实践》
《Java虚拟机规范》
其实我也读过这几本书,可是它们对虚拟机的解释也是基于一个外部模型,而没有深刻剖析虚拟机内部的实现原理。虚拟机是一个大而复杂的东西,实现虚拟机的人都是大牛级别的,若是不是参与过虚拟机的实现,应该不多有人能把它参透。本专栏后面的一些文章也参考了这三本书, 虽然讲解Java语法的书不可胜数, 可是深刻讲解虚拟机的书, 目前为止我就见过这三本,而且网上的资料也不是不少。
最后作一个总结:
1 虚拟机并不神秘,在操做系统的角度看来,它只是一个普通进程。
2 这个叫作虚拟机的进程比较特殊,它可以加载咱们编写的class文件。若是把JVM比做一我的,那么class文件就是咱们吃的食物。
3 加载class文件的是一个叫作类加载器的子系统。就比如咱们的嘴巴,把食物吃到肚子里。
4 虚拟机中的执行引擎用来执行class文件中的字节码指令。就比如咱们的肠胃,对吃进去的食物进行消化。
5 虚拟机在执行过程当中,要分配内存建立对象。当这些对象过期无用了,必需要自动清理这些无用的对象。清理对象回收内存的任务由垃圾收集器负责。就比如人吃进去的食物,在消化以后,必须把废物排出体外,腾出空间能够在下次饿的时候吃饭并消化食物。
更多关于深刻理解Java的文章, 请关注个人专栏 : http://blog.csdn.net/column/details/zhangjg-java-blog.html
更多关于Java和Android等其余技术的文章, 请关注个人博客: http://blog.csdn.net/zhangjg_blog
java语言是跨平台的,java虚拟机不是跨平台的。