JVM:java虚拟机
虚拟机:通过软件模拟的具有完整硬件功能,运行在完全隔离环境中的计算机系统。VMWare/Virtual Bos
JVM是通过软件模拟Java字节码指令集,JVM只保留了PC寄存器,而普遍的虚拟机有很多寄存器
从JDK1.3至今,HotSpot为默认JVM(垃圾回收机制和内存模型不同的虚拟机都不一样,下面讲的是HotSpot)
JVM不只能跑Java一门语言,如:Scala,Koltin
单核CPU:每一个具体时刻只有一个线程强占处理器资源
Java对内存的划分——>判断对象是否存活——>垃圾回收算法——>垃圾回收器(对垃圾回收算法的实现)——>JVM性能监测
问题:6块区域是哪6块,每块都干了那些事情,会产生那些异常
1、Java对内存的划分(基于HotSpot来说的)(按线程划分)——共6块
1.1、线程私有内存:每个线程都有,并且彼此之间完全隔离
1、程序计数器
1.1、程序计数器是比较小的内存空间,当前线程所执行的字节码的行号指示器
1.2、记录内容
若当前线程执行的是Java方法,计数器记录的是正在执行的JVM字节码指令地址;
若当前线程执行的是native方法,计数器值为空
1.3、程序计数器是唯一一块不会产生OOM(OutOfMemoryError)异常的区域
2、虚拟机栈——Java方法执行的内存模型
2.1、虚拟机栈描述Java方法执行的内存模型
2.2、每个方法执行的同时都会创建一个栈帧存储局部变量表、操作数栈、方法出口等信息。每个方法从调用到执行完毕的过程,对应一个栈帧在虚拟机栈的入栈与出栈过程
2.3、生命周期与线程相同:在创建线程时同时创建此线程的虚拟机栈,线程执行结束,虚拟机栈与线程一同被回收
局部变量表 : 存放了编译器可知的各种基本数据类型(8大基本数据类型)、对象引用(4字节)。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在执行期间不会改变局部变量表大小。
此区域一共会产生两种异常:
1、若线程请求的栈深度大于JVM允许的深度(-Xss设置栈容量),抛出StackOverFlowError异常(方法递归)(常见于单线程)
2、虚拟机在进行栈的动态扩展时,若无法申请到足够内存,抛出OOM(OutOfMemoryError)异常(常见于多线程)
3、本地方法栈
本地方法(native方法)执行的内存模型
在HotSpot虚拟机中,本地方法栈与虚拟机栈是同一块内存区域。
1.2、线程共享内存:所有线程共享此类型空间,并且此空间对所有线程可见
4、堆(GC堆):所有对象都在堆中
4.1、Java堆(Java Heap)是JVM管理的最大内存区域。在JVM启动时(执行java XXX命令是)创建,所有线程共享此内存,此内存中存放的都是对象实例以及数组。
4.2、Java堆是垃圾回收器管理的最主要内存区域。Java堆可以处于物理上不连续的内存空间。-Xmx设置堆最大值 -Xms设置堆最小值
4.3、若堆中没有足够的内存完成对象实例分配并且堆无法再次扩展时,抛出OOM异常
5、方法区
5.1、用于存储已被JVM加载的类信息、常量、静态变量等数据。JDK8以前,方法区也叫永久代,JDK8之后称为元空间(Meta Space)
5.2、方法区无法满足内存分配需求时,抛出OOM
永久代并不意味着数据进入方法区就永久存在,此区域的内存回收主要是针对常量池的回收以及对类型的卸载。
6、运行时常量池
6.1、运行时常量池是方法区的一部分,存放字面量(能直接写出来的。如:在Java中所有整形常量是int类型)与符号引用
字面量 : 字符串常量(JDK1.7后移动到堆中) 、final常量、基本数据类型的值。
符号引用 : 类、字段、方法的完全限定名、 名称、描述符。
类和结构的完全限定名、字段的名称和描述符、方法的名称和描述符。
public class Test {
public static void main(String[] args) { //主方法对应主线程,有一个线程就有一个虚拟机栈
Test test = new Test();
int a = 10;
}
}
虚拟机栈中存放了test和a
new Test()是对象实例,在堆上
public class Test、public static void main(符号引用在常量池)类的信息在方法区
10:字面量,基本数据类型的常量,在运行时常量池
Test:描述符
对象产生:符号引用——>类——>具体引用
Test test = new Test(); :通过符号引用拿到类信息再new
OOM:内存溢出:内存中的对象确实还应该存活,但由于对内存不够用产生的异常
内存泄露:无用对象无法被GC