什么是Java虚拟机
虚拟机是一种抽象化的计算机,经过在实际的计算机上仿真模拟各类计算机功能来实现的。Java虚拟机有本身完善的硬体架构,如处理器、堆栈、寄存器等,还具备相应的指令系统。JVM屏蔽了与具体操做系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就能够在多种平台上不加修改地运行。html
通常状况下咱们不须要知道虚拟机的运行原理,只要专一写java代码就能够了,这也正是虚拟机之因此存在的缘由--屏蔽底层操做系统平台的不一样而且减小基于原生语言开发的复杂性,使java这门语言可以跨各类平台(只要虚拟机厂商在特定平台上实现了虚拟机),而且简单易用。java
JVM是Java程序运行的环境,同时是一个操做系统的一个应用程序进程,所以它有本身的生命周期,也有本身的代码和数据空间.程序员
JVM体系主要是两个JVM的内部体系结构分为三个子系统和两大组件,分别是:类装载器(ClassLoader)子系统、执行引擎子系统和GC子系统,组件是内存运行数据区域和本地接口。算法
从进程的角度解释JVM
让咱们尝试从操做系统的层面来理解虚拟机。咱们知道,虚拟机是运行在操做系统之中的,那么什么东西才能在操做系统中运行呢?固然是进程,由于进程是操做系统中的执行单位。能够这样理解,当它在运行的时候,它就是一个操做系统中的进程实例,当它没有在运行时(做为可执行文件存放于文件系统中),能够把它叫作程序。数据结构
对命令行比较熟悉的同窗,都知道其实一个命令对应一个可执行的二进制文件,当敲下这个命令而且回车后,就会建立一个进程,加载对应的可执行文件到进程的地址空间中,而且执行其中的指令。下面对比C语言和Java语言的HelloWorld程序来讲明问题。多线程
#include <stdio.h> #include <stdlib.h> int main(void) { printf("hello world\n"); return 0; } 编译C语言版的HelloWorld程序 gcc HelloWorld.c -o HelloWorld 运行C语言版的HelloWorld程序 ./HelloWorld gcc编译器编译后的文件直接就是可被操做系统识别的二进制可执行文件,当咱们在命令行中敲下 ./HelloWorld这条命令的时候, 直接建立一个进程, 而且将可执行文件加载到进程的地址空间中, 执行文件中的指令。
public class HelloWorld { public static void main(String[] args) { System.out.println("HelloWorld"); } } 编译Java版的HelloWorld程序 javac HelloWorld.java 运行Java版的HelloWorld程序 java -classpath . 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文件中的信息看作对虚拟机的控制信息,也就是一种虚拟指令。post
虚拟机的三个主要功能系统
类加载器子系统:网站
这个子系统用来在运行时根据须要加载类。在Java虚拟机执行过程当中,只有他须要一个类的时候,才会调用类加载器来加载这个类,并不会在开始运行时加载全部的类。就像一我的,只有饿的时候才去吃饭,而不是一次把一年的饭都吃到肚子里。通常来讲,虚拟机加载类的时机,在第一次使用一个新的类的时候。spa
执行引擎子系统:
由虚拟机加载的类,被加载到Java虚拟机内存中以后,虚拟机会读取并执行它里面存在的字节码指令。虚拟机中执行字节码指令的部分叫作执行引擎。
垃圾收集子系统:
Java虚拟机会进行自动内存管理。具体说来就是自动释放没有用的对象,而不须要程序员编写代码来释放分配的内存。这部分工做由垃圾收集子系统负责。
虚拟机的内存结构
虚拟机的运行,必须加载class文件,而且执行class文件中的字节码指令。它作这么多事情,必须须要本身的空间。
加载的字节码,须要一个单独的内存空间来存放;
线程的执行,也须要内存空间来维护方法的调用关系;
存放方法中的数据和中间计算结果;
建立对象,建立的对象须要一个专门的内存空间来存放。
一、程序计数器:(线程私有)
每一个线程拥有一个程序计数器,在线程建立时建立,
指向下一条指令的地址
执行本地方法时,其值为undefined
说的通俗一点,咱们知道,Java是支持多线程的,程序先去执行A线程,执行到一半,而后就去执行B线程,而后又跑回来接着执行A线程,那程序是怎么记住A线程已经执行到哪里了呢?这就须要程序计数器了。所以,为了线程切换后可以恢复到正确的执行位置,每条线程都有一个独立的程序计数器,这块儿属于“线程私有”的内存。
二、Java虚拟机栈:(线程私有)
每一个方法被调用的时候都会建立一个栈帧,用于存储局部变量表、操做栈、动态连接、方法出口等信息。局部变量表存放的是:编译期可知的基本数据类型、对象引用类型。
每一个方法被调用直到执行完成的过程,就对应着一个栈帧在虚拟机中从入栈到出栈的过程。
在Java虚拟机规范中,对这个区域规定了两种异常状况:
(1)若是线程请求的栈深度太深,超出了虚拟机所容许的深度,就会出现StackOverFlowError(好比无限递归。由于每一层栈帧都占用必定空间,而 Xss 规定了栈的最大空间,超出这个值就会报错)
(2)虚拟机栈能够动态扩展,若是扩展到没法申请足够的内存空间,会出现OOM
三、本地方法栈:
(1)本地方法栈与java虚拟机栈做用很是相似,其区别是:java虚拟机栈是为虚拟机执行java方法服务的,而本地方法栈则为虚拟机执使用到的Native方法服务。
(2)Java虚拟机没有对本地方法栈的使用和数据结构作强制规定,Sun HotSpot虚拟机就把java虚拟机栈和本地方法栈合二为一。
(3)本地方法栈也会抛出StackOverFlowError和OutOfMemoryError。
四、Java堆:即堆内存(线程共享)
(1)堆是java虚拟机所管理的内存区域中最大的一块,java堆是被全部线程共享的内存区域,在java虚拟机启动时建立,堆内存的惟一目的就是存放对象实例几乎全部的对象实例都在堆内存分配。
(2)堆是GC管理的主要区域,从垃圾回收的角度看,因为如今的垃圾收集器都是采用的分代收集算法,所以java堆还能够初步细分为新生代和老年代。
(3)Java虚拟机规定,堆能够处于物理上不连续的内存空间中,只要逻辑上连续的便可。在实现上既能够是固定的,也能够是可动态扩展的。若是在堆内存没有完成实例分配,而且堆大小也没法扩展,就会抛出OutOfMemoryError异常。
五、方法区:(线程共享)
(1)用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
(2)Sun HotSpot虚拟机把方法区叫作永久代(Permanent Generation),方法区中最重要的部分是运行时常量池。
六、运行时常量池:
(1)运行时常量池是方法区的一部分,天然受到方法区内存的限制,当常量池没法再申请到内存时就会抛出OutOfMemoryError异常。
本文来自:https://www.cnblogs.com/wade-luffy/p/5752533.html#_label0