你们都知道,Java中JVM的重要性,学习了JVM你对Java的运行机制、编译过程和如何对Java程序进行调优相信都会有一个很好的认知。java
废话很少说,直接带你们来初步认识一下JVM。编程
JVM(Java Virtual Machine)是一个抽象的计算机,和实际的计算机同样,它具备指令集并使用不一样的存储区域,它负责执行指令,还要管理数据、内存和寄存器。数据结构
看到这里,可能不懂JVM的人,已经蒙圈了。不要紧,下面让我详细为你们介绍JVM的体系架构图,或许你会明白些。架构
简单来讲,JVM就是一个虚拟计算机。咱们都知道Java语言其中的一个特性就是跨平台的,而JVM就是Java程序实现跨平台的关键部分。Java编译器编译Java程序时,生成的是与平台无关的字节码(也就是*.class文件),所谓的平台无关是指编译生成的字节码不管是在Window、Linux、Mac系统都是可执行。也就是说Java编译生成的*.class文件不是面向平台的,而是面向JVM的。不一样平台上的JVM都是不一样的,可是他们都是提供了相同的接口。图一为Java的大体运行步骤:编程语言
图一学习
引用一个《疯狂Java讲义》中提到例子来帮助你们理解JVM的做用:spa
JVM的做用就像有两只不一样的铅笔,但须要把同一个笔帽套在两支不一样的笔上,只有为这两支笔分别提供一个转换器,这个转换器向上的接口相同,用于适应同一个笔帽;向下的接口不一样,用于适应两支不一样的笔。在这个类比中,能够近似地理解两支不一样的笔就是不一样的操做系统,而同一个笔帽就是Java字节码程序,转换器角色则对应JVM。相似地,也能够认为JVM分为向上和向下两个部分,全部平台的JVM向上提供给Java字节码程序的接口彻底相同,但向下适应的不一样平台的接口则互不相同。操作系统
上面咱们是初步介绍了JVM的做用,那么要深刻去了解JVM咱们就须要了解JVM的体系结构,请看图二:线程
图二指针
图二是JVM的体系架构图,接下让咱们一块儿来聊一聊每个部分都是什么意思。
1.类装载器子系统(ClassLoader)
负责加载class文件,class文件在文件开头有特定的文件标示,将class文件字节码内容加载到内存中,并将这些内容转换成方法区中的运行时数据结构而且ClassLoader只负责class文件的加载,至于它是否能够运行,则由Execution Engine决定。
Java编译生成的*.class文件就是经过ClassLoader进行加载的,那么这里就会有几个问题:
实际上,class文件在文件的开头是有特定的文件标识的,随便编写一个Java程序,编译生成一个class文件,打开后你都能看到以下内容:
cafe babe就是class文件的一个标识,ClassLoader负责加载有cafe babe的class文件,它将class文件字节码内容加载到内存中,并将这些内容转换成方法区中的运行时的数据结构而且ClassLoader只负责class文件的加载,至于它是否能够运行,则由Execution Engine决定,请看图三:
图三
Car.class文件经过ClassLoader进行加载到内存中,Car Class在内存中就至关一个模板,咱们能够经过这个模板能够实例化成不一样的实例car一、car二、car3。
不知你们会不会有一个疑问,ClassLoader加载Car.class在Java中是用什么类型的加载器加载的呢?在解答这个问题前咱们先写个简单的代码看看:
//new一个Car对象 Car car = new Car(); //获得ClassLoader ClassLoader classLoader = car.getClass().getClassLoader(); //打印结果 System.out.println(classLoader);
结果为:
咱们再来看看另一组代码:
//new两个不一样的对象 Car car = new Car(); String string = new String(); //获得ClassLoader ClassLoader classLoader1 = car.getClass().getClassLoader(); ClassLoader classLoader2 = string.getClass().getClassLoader(); //打印结果 System.out.println(classLoader1); System.out.println(classLoader2);
结果为:
从上面咱们能够知道,ClassLoader的打印结果一个是“sun.misc.Launcher$AppClassLoader@18b4aac2”,一个则是“null”,这是怎么回事呢,细心的朋友就能够发现这两个不一样的对象中,其中car对象是咱们本身写的一个类,string对象是系统自带的一个类。简单来讲就是ClassLoader会根据不一样的类选择不一样的类加载器去进行加载。这里就牵扯到了ClassLoader的分类
ClassLoader的类别:
通常咱们本身所写的类用的类加载器都是AppClassLoader,就是上图所示的“sun.misc.Launcher$AppClassLoader@18b4aac2”,而为何string这个对象是”null“呢?实际上,这个“null”指的就是使用BootStrap这个加载器。
那可能有人有疑问,本身定义的类用AppClassLoader,能理解,由于car这个对象输出的类加载器名字中有AppClassLoader这个字样,可是为何string这个对象是”null“,从哪里可用体现是用BootStrap这个加载器呢?是这样的,BootStrap累加载器至关于扩展类加载器、应用程序类加载器的祖宗,如果用了BootStrap,因为BootStrap上一级已经没有了,因此就用“null”来表示
其实咱们能够找一下String这个类在JDK的位置:
$JAVA_HOME/jre/lib/rt.jar/java/lang
全部在这个路径$JAVA_HOME/jre/lib/rt.jar这个jar包下的类都是用BootStrap来加载的。
下面请看图4:
图四
这张图就能够很清晰得看到:
1.全部在$Java_Home/jre/lib/rt.jar是经过BootStrap加载的
2.全部在$Java_Home/jre/lib/ext/*.jar是经过Extension加载的
3.全部在$CLASSPATH是经过SYSTEM加载的(应用程序类加载器也叫系统类加载器,加载当前应用的classpath的全部类)
接下来咱们再来看一个例子:
若是建立一个java.lang包,而后建立String类,打印一句话执行会怎么样呢?
package java.lang; public class String { public static void main(String[] args) { System.out.println("Hello World"); } }
效果以下:
能够看到程序报错了,说是找不到main方法,但是明明就有main方法为何没有执行呢?这里就涉及了双亲委派机制
双亲委派机制:
当一个类收到了类加载请求,他首先不会尝试本身去加载这个类,而是把这个请求委派给父类去完成,每个层次类加载器都是如此,所以全部的加载请求都应该传送到启动类加载器中,只有当父类加载器反馈本身没法完成这个请求的时候(在它的加载路径下没有找到所需加载的Class),子类加载器才会尝试本身去加载。
因此它实际的运行过程是这样的:
因此上面的例子,他会找到jdk中java.lang.String这个类,这个类确实是没有定义main方法,简单来讲它执行的类是JDK中java.lang.String这个类,而不是咱们本身定义的类。
那用双亲委派机制有什么好处呢:
采用双亲委派的一个好处是好比加载位于 rt.jar 包中的类 java.lang.Object,不论是哪一个加载器加载这个类,最终都是委托给顶层的启动类加载器进行加载,这样就保证了使用不一样的类加载器最终获得的都是一样一个 Object对象。
2.执行引擎(Execution Engine)
执行引擎负责解释命令,提交给操做系统执行,这里对执行引擎就不作过多的解释了,只要知道他是负责解释命令的便可。
3.本地方法接口(Native Interface)和本地方法栈(Native Method Stack)
目前该方法使用的愈来愈少了,除非是与硬件有关的应用,好比经过Java程序驱动打印机或者Java系统管理生产设备,在企业级应用中已经比较少见。由于如今的异构领域间的通讯很发达,好比可使用 Socket通讯,也可使用Web Service等等,很少作介绍。
若是在程序中有见到native关键字,就表明不是Java能完成的事情了,须要加载本地方法库才能完成
4.PC寄存器(Program Counter Register)
每一个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向下一条指令的地址,也即将要执行的指令代码),由执行引擎读取下一条指令,是一个很是小的内存空间,几乎能够忽略不记。
这块内存区域很小,它是当前线程所执行的字节码的行号指示器,字节码解释器经过改变这个计数器的值来选取下一条须要执行的字节码指令。
若是执行的是一个Native方法,那这个计数器是空的。
PC寄存器用来完成分支、循环、跳转、异常处理、线程恢复等基础功能。因为使用的内存较小,因此不会发生内存溢出(OutOfMemory)错误。
那么这篇文章先讲到这里,下篇文章中咱们再继续来聊一聊方法区、栈和堆..........