首先需要说明的时jvm运行在操作系统之上,与硬件无关。
本文所讲是基于JDK1.8 SUN出品的HotSpot虚拟机
- 既然要讲解jvm,那首先就要知道jvm时什么?
- jvm全称Java virtual Machine(Java虚拟机)
它的执行流程如下
- classLoader(类加载器)将class文件加载到运行数据区,这个运行数据区是jvm虚拟机在内存中划分出来的一块区域。供自己分配,使用。
- 运行数据区:
- Method Area(方法区)
- Java Stack(Java栈)
- Native Stack(本地栈)
- Heap(堆)
- Program Counter Register(程序计算器)
- 执行引擎
- 本地方法库
classLoader(类加载器)
- 负责加载class文件,class文件,在文件的开头有一个特定的文件标识(cofe babe开头)。classLoader将字节码文件内容加载到内存中。并将这些字节码文件转换成方法区中运行时数据结构,至于它是否可以正常的运行,则有Excution Engine(执行引擎)决定。
- classLoader就好比时快递员,负责文件的加载。所以对于classLoader它是有好多种的。
- classLoader粗说三种,细说四种(加上用户自定义加载器)。
双亲委派机制
- 当一个类收到了类加载请求,它首先不会尝试自己去加载这个类。而是把这个类委派给他的上一级去完成。并且每一层次类加载器都是如此。因此所有类的加载请求都是从最上级开始。当最上级的类加载器不能完成时(在它的加载路径下没有找到所需的class文件),会将这个请求交给他的下一级。
- 可以通过代码找到对应所属类的加载器
- 对于最上级的启动类加载器:当我们查看时打印为空 Todo
- 采用双亲委派机制的好处是:比如加载rt.jar包中的类Java.lang.Object不管用那个加载器加载,最终都是由Bootstrap classLoader这个加载器进行加载,保证最总得到的都是同一个object对象。
- 关于类加载器的四个知识点:
- 什么是类加载器
- 有几种类加载器
- 双亲委派机制
- 沙箱安全机制(双亲委派机制的好处)
Native Interface(本地接口)
- 本地接口的作用时融合不同的编程语言为Java所用,它的初衷是融合C/C++程序,Java诞生的时候是C/C++横行的年代,想要立足,必须要调用C/C++程序,于是就在内存中专门开辟了一块区域处理标记为native的空间。它的具体做法是 Native Method Stack中登记 native方法,在Execution Engine 执行时加载native libraies。
Program Counter Register(程序计数器 又叫 pc寄存器)
- Program Counter Register也叫pc寄存器
- 在cpu中的一小块位置
- 通俗的解释为火车车厢之间的钩子,班级的值日表(用来记录顺序)
- 本质上为一个指针。当一个方法执行完,指向下一个方法。
- 官方说法为:每一个线程都有一个程序计数器,是线程私有的。它就是一个指针,指向方法区中的方法字节码(用来存储指向下一条指令的地址,也就是即将要执行的指令代码),由执行引擎读取下一条指令,是非常小的一块内存空间,几乎可以忽略不记。它是当前线程所执行的字节码的行号指示器,字节码解释器通过改变这个计数器的值来选取下一条需要执行的字节码指令。如果执行一个Native方法,那这个计数器是空的。用以来完成分支,跳转,循环,异常处理。线程恢复等基本功能。防止发生内存溢出(OutOfMemory=00M)错误。
方法区
- 首先需要说明的一点是在物理成面上方法区在堆中,常量池在方法区中。但在逻辑上方法区与堆又是分开的。
- 所以可以知道方法区在内存中是被线程公有的,与程序Java栈,程序计算器想法,并不是线程私有的。
- 这里需要补充一点的是:实例变量,实例方法
- 实例变量:在类的声明中,属性是用变量来表示的,这种变量就是实例变量,在类中,但在类的方法外的变量。类的每一个对象维护它自己的一份实例变量的副本。当一个对象被实例化后,每个实例变量的值也就跟着确定下来。实例变量在对象的创建时创建,在对象销毁的时候跟着销毁。实例变量具有默认值。数值型变量的默认值为0,布尔型变量的默认值为false,引用类型的值为null。(在内存空间中存储的是0,不同的数据类型他的表现形式是不同的)。
- 实例方法 :Java中分为类方法和实例方法,类方法有static修饰,为静态方法,是类的方法。而实例方法是对象的方法。只有当对象创建完之后。才能通过对象调用实例方法。
- 官方说法:供各线程共享的,在运行时公共的内存区域。它存储每一个类的结构信息(不同且唯一的模板)。例如Runtime Constant Pool(运行时的常量池),类的属性(字段),构造函数,普通方法和方法的数据的字节码内容。上面说的时规范。在不同的Java虚拟机中有不同的实现。But实例变量存在堆内存中和方法区无关。
- 在这里可有会有疑问?类的属性(字段)在方法区中,可又说实例变量是存在堆中的。这不是相矛盾吗?注意方法区中存放的是模板,实例变量(这里可理解是你赋值之后的结果)。
stack(栈,Java栈 也叫做 虚拟机栈)
- 栈管运行,堆管存储。
- 栈也叫栈内存,主管Java程序的运行,是在线程创建时创建,它的生命周期时随着线程的生命周期,线程结束栈内存也就释放了,所以对于栈内存来讲,它也就不存在垃圾回收问题了,生命周期与线程一致,是线程私有的。
- 栈内存中存放:8种基本数据类型的变量,对象的引用变量+实例方法都是在栈内存中分配的。
- 栈的存储是以栈帧体现的,栈帧中存放三种数据。1.Local Variables(本地变量):输入参数和输出参数+方法中的变量。2.Operand Stack(栈操作):记录出栈和进栈的操作。3.栈帧数据(Frame Date):包括类文件,方法。
- 栈运行原理:Todo
Heap(堆)
- 一个JVM实例只存在一个堆内存中。堆内存的空间大小是可以调节。类加载器读取了类文件后,需要把类,方法,常变量存放在堆内存中。保存所有引用类型的真实信息。
- 图 Todo
- 新生区