从JVM的总体上看,它解决了3个问题:
Java程序的内存管理(GC运行时数据区)。
JavaClass二进制字节流的加载(ClassLoader)。
Java程序的执行(执行引擎)。
如下图所示:
虚拟机基本架构
在我们的日常开发中,最和我们息息相关的就是1和2了,比如GC调优,内存故障排查,再比如使用自定义的类加载器去实现一些特定的逻辑,就如我们之前分析的tomcat源码中的类加载器用于隔离不同版本的相同类库,必须要使用自定义的类加载器。
1.Java程序的内存管理(GC运行时数据区)
1.1.运行时数据区
从上图中可以看出,虚拟机内存管理主要由几个部分组成:
Java堆(存放对象的地方)
Java栈(在HotSpot实现中,本地方法栈和虚拟机栈合二为一)
PC寄存器(也称为程序计数器)
方法区(JDK8中已经将方法区去除,改为元数据区,解决了方法区----也称为永久代的内存溢出)。
直接内存(大小不限制于Java堆,直接向操作系统申请,使用场景于读写频繁的场合,例如NIO)
1.2.垃圾回收系统
Java程序员之所以不需要显式的释放内存,一切都归功于GC,GC解放了Java程序员的双手。GC系统会在后台清除无用的实例对象,释放内存空间。
而JVM中的GC可谓是一个庞大的系统,其中分为几个部分:
判断什么是垃圾对象
垃圾回收算法
各种各样的垃圾收集器
我们将会在后面慢慢讲解。
2.JavaClass二进制字节流的加载(ClassLoader)
2.1类加载器
对于Java虚拟机来说,Class文件是一个重要的接口,无论使用何种语言进行软件开发,只要能将源文件编译为正确的Class文件,那么这种语言就可以在Java虚拟机上运行。可以说,Class文件就是虚拟机的基石。
如图所示:
各种语言都可以在JVM上运行
从上图可以看出,虚拟机不拘泥于Java语言,任何一个源文件只要能编译成Class文件的格式,就可以在JVM上运行!Class文件格式就像是一个接口,只要遵守这个接口,就能够在JVM上运行。
2.2类加载器的工作流程
Class文件通常是以文件的方式存在(任何二进制流都可以是Class类型),但只有能被JVM加载后才能被使用,才能运行编译后的代码。系统装在Class类型可以分为加载,链接和初始化三个步骤。其中,链接也可分为验证,准备和解析3步骤。如图所示:
Class文件转载过程
2.3类装载的条件
JVM不会无故装载Class文件,只有在必要的时候才装载,哪几个时候呢?
当创建一个类的实例是,比如使用new关键字,或者通过反射,克隆,反序列化。
当调用类的静态方法时,即当使用字节码invokstatic指令。
当使用类或接口的静态字段时(final常量除外),比如,使用getstatic或者pustatic指令。
当时用Java.lang.reflect包中的方法反射类的方法时。
当初始化子类,要求先初始化父类。
作为启动虚拟机,含有main()方法的那个类。
以上6种情况属于主动调用,主动调用会触发初始化,还有一种情况是被动调用,则不会引起初始化。
2.4类加载器的双亲委派模型
先来看一个著名的图:
类加载器双亲委派模型
虚拟机为了保护应用程序内出现多个类名相同的类对象,因此发明了这个机制:当JVM需要使用一个类时,在判断类是否已经被加载时,会先从当前底层类加载器进行判断。当系统需要加载一个类时,会从顶层类开始加载,依次向下尝试,直到成功,否则抛出ClassNotFound异常。
#总结
这篇文章主要是为了后面的详细文章做一个大纲,因此只是讲述以JVM的一些基本的框架和概念,后面讲详细的讲述内部细节。让我们学习虚拟机更加的有条理。