1 什么是JVM
(1)Java Virtual Machine:Java虚拟机,用来保证Java语言跨平台
(2)Java虚拟机可以看成是一台抽象的计算机,如同真实的计算机那样,它有自己的指令集以及各种运行时内存区域,
(3)Java虚拟机与Java语言并没有必然的联系,它只与特定的二进制文件格式(class文件格式)所关联
(4)Java虚拟机就是一个字节码文件翻译器,它将字节码文件翻译成各个系统对应的机器码,确保字节码文件能在各个系统正确运行。
2 为什么学习JVM
(1)面试
(2)解决问题要做到“知其然”,也要知其所以然
(3)中高级程序员的必备技能
3 JVM体系结构
(1)类加载器
(2)运行时数据区
(3)执行引擎
4 Java内存结构
4.1 程序计数器(PC Register)
线程私有
作用:保存当前执行指令的地址,一旦指令执行,程序计数器将更新到下一条指令。
例如:程序计数器当前记录的是0,当0这条指令被CPU执行,程序计数器将更新成下一条指令的地址3.
4.2 虚拟机栈(JVM Stacks)
后进先出,线程私有
(1)每个线程运行时所需要的内存空间,称为虚拟机栈
(2)每个栈由多个栈帧组成
(3)每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法
如图:每个方法对应一个栈帧,每个线程只能有一个活动的栈帧
栈帧的组成内容:局部变量表、操作数栈、动态链接、方法返回地址
- 局部变量表:
存放局部变量的列表 - 操作数栈:
也称为操作栈, 是一个后进先出的栈,当一个方法刚刚开始执行时,其操作数栈是空的,随着方法执行和字节码指令的执行,会从局部变量表或对象实例的字段中复制常量或变量写入到操作栈,再随着计算的进行,将栈中元素出栈到局部变量表或方法的调用者,也就是出栈、入栈的操作。简单理解,操作数栈是线程的实际操作台。 - 动态链接
简单的理解为指向运行时常量池的引用
在class文件里,描述一个方法调用其他方法或者访问其他成员变量,是通过符号引用来表示的,动态链接的作用就是将这些符号引用所表示的方法转换为实际方法的直接引用。 - 方法返回地址
方法调用的放回,包括正常返回(有返回值)和异常返回(无返回值),不同的返回类型有不同的指令。无论采用哪种方式退出,在方法退出后都要返回到方法被调用的位置,程序才能继续执行,方法放回时需要在当前栈帧中保存一些信息,用来帮他恢复他上层方法执行状态。
4.2.1 栈帧过多导致栈内存溢出
可以通过设置Xss来直观感受,Xss:规定了每个线程虚拟机栈的大小。
//-Xss256k:次数变少 最终打印count是2718
//-Xss10m:次数变多,最终打印count是258270
4.2.2 栈帧过大导致栈内存溢出
循环引用可能会引起栈帧过大导致内存溢出
4.3 本地方法栈(Native Method Stacks)
线程私有
本地方法栈的功能和特点类似于虚拟机栈,也是线程私有的,不同的是,本地方法栈服务的对象是JVM执行的native方法,而虚拟机栈服务的对象是JVM执行的java方法。
4.4 堆(Heap)
堆是用于存放对象的内存区域
堆的特点
(1)堆是所有线程共享的一块内存区域,在虚拟机启动时创建
(2)堆的区域是用来存放对象实例的,因此也是垃圾收集器管理的主要区域
(3)堆在逻辑上划分为“新生代”和“老年代”,新生代分为Eden区、ServivorFrom、ServivorTo三个区域。
(4)堆一般实现成大小可扩展的,使用“-Xms”与“-Xmx”控制堆的最小于最大内存
4.4.1 堆内存溢出
错误提示:java.lang.OutOfMemoryError: Java heap space
错误原因:
(1)内存真不够,通过调整堆内存大小解决
(2)存在死循环,通过修改代码解决
4.4.2 堆内存诊断
- jps:查看系统中有哪些Java进程
- jmap工具:查看堆内存某一时刻占用情况
例如监测以下内容:jhsdb jmap --heap --pid 11876
public class Test3 {
public static void main(String[] args) throws InterruptedException{
System.out.println("1................");
Thread.sleep(30000);
byte [] bys = new byte[1024*1024*10];//10M
System.out.println("2................");
Thread.sleep(10000);
bys = null;
System.gc();
System.out.println("3...............");
Thread.sleep(1000000);
}
}
Attaching to process ID 16104, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 9.0.4+11
using thread-local object allocation.
Garbage-First (G1) GC with 8 thread(s)
Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 2111832064 (2014.0MB)
NewSize = 1363144 (1.2999954223632812MB)
MaxNewSize = 1266679808 (1208.0MB)
OldSize = 5452592 (5.1999969482421875MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 1048576 (1.0MB)
Heap Usage:
G1 Heap:
regions = 2014
capacity = 2111832064 (2014.0MB)
used = 15728640 (15.0MB)
free = 2096103424 (1999.0MB)
0.7447864945382324% used
G1 Young Generation:
Eden Space:
regions = 5
capacity = 27262976 (26.0MB)
used = 5242880 (5.0MB)
free = 22020096 (21.0MB)
19.23076923076923% used
Survivor Space:
regions = 0
capacity = 0 (0.0MB)
used = 0 (0.0MB)
free = 0 (0.0MB)
0.0% used
G1 Old Generation:
regions = 11
capacity = 104857600 (100.0MB)
used = 10485760 (10.0MB)
free = 94371840 (90.0MB)
10.0% used
3291 interned Strings occupying 285888 bytes.
- jconsole工具
图形界面的,内置java性能分析器,多功能的检测工具,可连续监测。
4.5 方法区(Method Area)
作用:存储每个类的结构
例如运行时常量池,字段和方法数据,以及方法和构造函数的代码,包括用于类和实例初始化以及接口的初始化的特殊方法等。
1.6和1.8的区别
-
Java6方法去溢出:
-XX:MaxPermSize=8m
永久代内存溢出:java.lang.OutOfMemoryError: PermGen space -
Java8内存溢出:
-XX:MaxMetaspaceSize=10m -XX:-UseCompressedOops
元空间内存溢出:java.lang.OutOfMemoryError: Metaspace