# JVM是运行在操作系统之上的,它与硬件没有直接的交互

1、总体结构

2、类装载器ClassLoader 负责加载class文件,class文件在文件开头有特定的文件标识(通过文本打开之后发现是 cafe babe开头 咖啡豆),将这些内容转换成方法区中的运行时数据结构(模板数据结构),并且ClassLoader只负责Class文件的加载,至于它是否可以运行,则由Exeution Engine决定 类加载器包括了 虚拟机自带的加载器 启动类加载器:BootStrap 这是C++写的加载器,负责加载Java JDK中原生的类,比如Object

JDK自带的类 走的是Bootstrap加载器

双亲委派 当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每一个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载器中,只有当父类加载器反馈自己无法完成这个请求的时候(在它的加载路径下没有找到所需加载的Class),子类加载器才会尝试自己去加载。 采用双亲委派的一个好处就是比如加载位于rt.jar包总的类 java.lang.Object,不管是哪个加载器加载这个类,最终都是委派给顶层的启动类加载器进行加载的,这样就包正了使用不同的加载加载器最终得到的都是同一个Object对象

Execution Engine 执行引擎负责解释命令,提交操作系统执行

3、Native Interface 本地接口 本地接口的作用是为了融合不同的编程语言为Java所用,它的初衷是融合C/C++程序,Java诞生的时候是在C/C++横行的时候,想要立足,必须由调用C/C++程序,于是就在内存中专门开辟了一块区域处理native方法,在execution engine执行时加载native libraies(其他语言的库) 但是目前该方法使用的越来越少,除非是硬件有关的应用,比如通过Java程序驱动打印机或者Java系统管理生产设备,在企业级应用中已经比较少见。因为现在异构领域的通信很发达,比如可以使用Socket通信,也可以使用Web Service等等 Native Method Stack 它的具体做法就是Native Method Stack中登记native方法,在Excution Engine执行时加载本地方法库 案例 比如创建一个线程时用的 thread.start()方法 其底层使用的就是native关键字修饰的 start0()方法

由此观之,一旦这个方法被native修饰,就意味着这个方法已经是Java处理不了的了,需要去调用其他语言的本地库或者直接跟操作系统资源有关

4、PC寄存器 每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向下一条命令的地址,也即将要执行的指令代码),由执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计。 这块内存区域很小,它是当前线程锁执行的字节码的行号指示器,字节码解释器通过改变这个计数器的值来选取下一条需要执行的字节码指令。 如果执行的是一个native方法,那这个计数器是空的。 用以完成分支、循环、跳转、异常处理、线程回复等基础功能。不会发生内存溢出错误。 PC寄存器介绍 JVM中的程序计数寄存器(Program Counter Register)中,Register的命名源于CPU的寄存器,寄存器存储指令相关的现场信息。CPU只有把数据装载到寄存器才能够运行。 这里,并非是广义上所指的物理寄存器,或许将其翻译为PC计数器(或指令计数器)会更加贴切(也称为程序钩子),并且也不容易引起一些不必要的误会。JVM中的PC寄存器是对物理PC寄存器的一种抽象模拟。 它是一块很小的内存空间,几乎可以忽略不记。也是运行速度最快的存储区域。 在JVM规范中,每个线程都有它自己的程序计数器,是线程私有的,生命周期与线程的生命周期保持一致。 任何时间一个线程都只有一个方法在执行,也就是所谓的当前方法。程序计数器会存储当前线程正在执行的Java方法的JVM指令地址;或者,如果是在执行native方法,则是未指定值(undefned)。 它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。 字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。 它是唯一一个在Java虚拟机规范中没有规定任何OutofMemoryError情况的区域。

以上知识点总结: 1.JVM系统架构图 2.有哪几种类加载器? 3.双亲委派机制? 4.沙箱机制? 5.Native native关键字 为什么被native标识的方法有声明无实现 native方法栈 6.PC寄存器 这并不是真正物理上的寄存器而是Java虚拟出来的 记录了方法之间的调用和执行情况,记录了下一条指令的地址,也就是将要执行的指令代码 它是当前线程所执行的字节码的行号指示器 5、方法区 所有线程共享,存在垃圾回收,方法区绝对不是放方法的地方,方法区是供各个线程共享时运行时内存区域。它存储了每一个类的结构信息,例如运行时常量池、字段和方法数据、构造函数和普通方法的字节码内容。 方法区讲的是种规范,在不同虚拟机里实现是不一样的,最典型的就是永久代和元空间两种实现 比如下面的理解案例 =左边的是规范,右边的就是具体实现

实例变量存在堆内存中和方法区无关

6、Java栈(Java stack) 程序=数据结构+算法 程序=框架+业务逻辑

栈管理运行,堆管理存储 栈也叫栈内存,主管Java程序的运行,是在线程创建时创建,它的生命周期跟随线程的生命周期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束该栈就释放,生命周期和线程是一致的,是线程私有的。 其存储了 8种基本数据类型+对象的引用变量+实例方法,都是在函数的栈内存中分配的 栈存储什么? 栈帧主要保存3类数据 本地变量:输入参数和输出参数 以及方法内的变量 栈操作:记录出栈入栈的操作 栈帧数据:包括类文件、方法等等 每执行一个方法都会产生一个栈帧,保存到栈(后进后出)的顶部,顶部栈就是当前的方法,该方法执行完毕后会自动将此栈帧出栈。 Exception in thread"main" java.lang.StackOverflowError(栈溢出,这是一个错误信息) 栈+堆+方法区的交互关系 HotSpot是Sun公司的JVM规范的一个版本 HotSpot是使用指针的方式来访问对象的: Java堆中会存放访问 类元数据的地址 reference存储的就直接是对象的地址