从Java平台的逻辑结构上来看,咱们能够从下图来了解JVM: java
从上图能清晰看到Java平台包含的各个逻辑模块,也能了解到JDK与JRE的区别 算法
对于JVM自身的物理结构,咱们能够从下图鸟瞰一下: tomcat
对于JVM的学习,在我看来这么几个部分最重要: 多线程
Java代码编译是由Java源码编译器来完成,流程图以下所示: 并发
Java字节码的执行是由JVM执行引擎来完成,流程图以下所示: 学习
Java代码编译和执行的整个过程包含了如下三个重要的机制: spa
Java源码编译机制 线程
Java 源码编译由如下三个过程组成: 指针
流程图以下所示: 调试
最后生成的class文件由如下部分组成:
类加载机制
JVM的类加载是经过ClassLoader及其子类来完成的,类的层次关系和加载顺序能够由下图来描述:
1)Bootstrap ClassLoader
负责加载$JAVA_HOME中jre/lib/rt.jar里全部的class,由C++实现,不是ClassLoader子类
2)Extension ClassLoader
负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包
3)App ClassLoader
负责记载classpath中指定的jar包及目录中class
4)Custom ClassLoader
属于应用程序根据自身须要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader
加载过程当中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap ClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只全部ClassLoader加载一次。而加载的顺序 是自顶向下,也就是由上层来逐层尝试加载此类。
类执行机制
JVM是基于栈的体系结构来执行class字节码的。线程建立后,都会产生程序计数器(PC)和栈(Stack),程序计数器存放下一条要执行的指 令在方法内的偏移量,栈中存放一个个栈帧,每一个栈帧对应着每一个方法的每次调用,而栈帧又是有局部变量区和操做数栈两部分组成,局部变量区用于存放方法中的 局部变量和参数,操做数栈中用于存放方法执行过程当中产生的中间结果。栈的结构以下图所示:
JVM内存组成结构
JVM栈由堆、栈、本地方法栈、方法区等部分组成,结构图以下所示:
1)堆
全部经过new建立的对象的内存都在堆中分配,其大小能够经过-Xmx和-Xms来控制。堆被划分为新生代和旧生代,新生代又被进一步划分为Eden和Survivor区,最后Survivor由From Space和To Space组成,结构图以下所示:
每一个线程执行每一个方法的时候都会在栈中申请一个栈帧,每一个栈帧包括局部变量区和操做数栈,用于存放这次方法调用过程当中的临时变量、参数和中间结果
本地方法栈用于支持native方法的执行,存储了每一个native方法调用的状态
4)方法区
存放了要加载的类信息、静态变量、final类型的常量、属性和方法信息。JVM用持久代(Permanet Generation)来存放方法区,可经过-XX:PermSize和-XX:MaxPermSize来指定最小值和最大值
垃圾回收机制
JVM分别对新生代和旧生代采用不一样的垃圾回收机制
新生代的GC:
新生代一般存活时间较短,所以基于Copying算法来进行回收,所谓Copying算法就是扫描出存活的对象,并复制到一块新的彻底未使用的空间 中,对应于新生代,就是在Eden和From Space或To Space之间copy。新生代采用空闲指针的方式来控制GC触发,指针保持最后一个分配的对象在新生代区间的位置,当有新的对象要分配内存时,用于检查 空间是否足够,不够就触发GC。当连续分配对象时,对象会逐渐从eden到survivor,最后到旧生代,
用java visualVM来查看,能明显观察到新生代满了后,会把对象转移到旧生代,而后清空继续装载,当旧生代也满了后,就会报outofmemory的异常,以下图所示:
在执行机制上JVM提供了串行GC(Serial GC)、并行回收GC(Parallel Scavenge)和并行GC(ParNew)
1)串行GC
在整个扫描和复制过程采用单线程的方式来进行,适用于单CPU、新生代空间较小及对暂停时间要求不是很是高的应用上,是client级别默认的GC方式,能够经过-XX:+UseSerialGC来强制指定
2)并行回收GC
在整个扫描和复制过程采用多线程的方式来进行,适用于多CPU、对暂停时间要求较短的应用上,是server级别默认采用的GC方式,可用-XX:+UseParallelGC来强制指定,用-XX:ParallelGCThreads=4来指定线程数
3)并行GC
与旧生代的并发GC配合使用
旧生代的GC:
旧生代与新生代不一样,对象存活的时间比较长,比较稳定,所以采用标记(Mark)算法来进行回收,所谓标记就是扫描出存活的对象,而后再进行回收未 被标记的对象,回收后对用空出的空间要么进行合并,要么标记出来便于下次进行分配,总之就是要减小内存碎片带来的效率损耗。在执行机制上JVM提供了串行 GC(Serial MSC)、并行GC(parallel MSC)和并发GC(CMS),具体算法细节还有待进一步深刻研究。
以上各类GC机制是须要组合使用的,指定方式由下表所示:
指定方式 |
新生代GC方式 |
旧生代GC方式 |
-XX:+UseSerialGC |
串行GC |
串行GC |
-XX:+UseParallelGC |
并行回收GC |
并行GC |
-XX:+UseConeMarkSweepGC |
并行GC |
并发GC |
-XX:+UseParNewGC |
并行GC |
串行GC |
-XX:+UseParallelOldGC |
并行回收GC |
并行GC |
-XX:+ UseConeMarkSweepGC -XX:+UseParNewGC |
串行GC |
并发GC |
不支持的组合 |
1、-XX:+UseParNewGC -XX:+UseParallelOldGC 2、-XX:+UseParNewGC -XX:+UseSerialGC |