JVM
JVM 规范是一种高度抽象行为的描述,而不是具体虚拟机的实现,有很多根据规范实现的具体的虚拟机,如 Oracle 的 Hotspot。Java 虚拟机是 Java 语言实现硬件无关、操作系统无关的关键部分。Java 虚拟机和java 语言没有必然的联系,它只是与特定的二进制文件 class 文件有关联。class 文件是一种能够被 Java 虚拟机所执行的二进制文件,常常以文件的形式存储。
JVM 规范
Animorphic Smalltalk 虚拟机 -> Hotspot 虚拟机
JVM 64bit 没有 client 模式 只有 server 模式
JVM 的数据类型也分为两种:基本类型、引用类型。
Java 虚拟机希望尽可能多的类型检查能在程序运行之前完成,换句话说,编译器应当在编译 期间尽最大努力完成可能的类型检查,使得虚拟机在运行期间无需进行这些操作。
运行时数据区
Java 虚拟机定义了若干种程序运行期间会使用到的运行时数据区,其中有一些会随着虚拟机启动而创建,随着虚拟机退出而销毁。另外一些则是与线程一一对应的,这些与线程对应的数据区域会随着线程开始和结束而创建和销毁。
虚拟机定义的运行时数据区,就是我们常说的内存结构。
运行时数据区中的有些数据是一直存在的,被所有的线程共享,而有些数据是线程私有的,随着线程开始而创建,结束而销毁。
- 公有部分包括:方法区、运行时常量池、Java堆。
- 私有部分包括:Java虚拟机栈、本地方法栈、PC寄存器。
方法区 (线程共享)元空间
在Java虚拟机中,方法区(Method Area)是可供各条线程共享的运行时内存区域。它存储了每一个类的结构信息,例如运行时常量池(Runtime Constant Pool)、字段和方法数据、构造函数和普通方法的字节码内容、还包括一些在类、实例、接口初始化时用到的特殊方。
在HotSpot虚拟机中,JDK1.7版本称其为永久代(Permanent Generation),而在JDK1.8则称之为元空间(Metaspace)。
可能有异常
- OutOfMemoryError
如果方法区的内存空间不能满足内存分配请求,那Java虚拟机将抛出一个OutOfMemoryError 异常。
运行时常量池 (分配在方法区内)
运行时常量池(Runtime Constant Pool)是每一个类或接口的常量池(Constant_Pool)的运行时表示形式,每一个运行时常量池都分配在 Java 虚拟机的方法区之中,在类和接口被加载到虚拟机后,对应的运行时常量池就被创建出来。
JAVA 堆 (线程共享)
在 Java 虚拟机中,堆(Heap)是可供各条线程共享的运行时内存区域,也是供所有类实例和数组对象分配内存的区域。
可能有异常
- OutOfMemoryError
如果实际所需的堆超过了能提供的最大容量,那Java虚拟机将会抛出一个OutOfMemoryError 异常。
JAVA 虚拟机栈 (线程私有)
每一个虚拟机线程都有自己私有的虚拟机栈,这个虚拟机栈和线程是同时创建的,用于存储栈帧,就是用于存储局部变量与一些过程结果的地方。每一个方法在执行的时候都会创建一个栈帧,一个方法从开始调用到执行结束的过程,就对应一个栈帧从入栈到出栈的过程。
可能有两个异常
- StackOverFlowError
- OutOfMemoryError
Java8 中默认一个虚拟机栈的空间大小是 1MB 如果存放的栈帧太多导致1MB内存耗尽会 StackOverFlowError
如果在一开始申请分配 1MB 大小空间时内存不够,会OutOfMemoryError。
本地方法栈(线程私有)
如果 Java 虚拟机不支持 natvie 方法,并且自己也不依赖传统栈的话,可以无需支持本地方法栈,如果支持本地方法栈,那这个栈一般会在线程创建的时候按线程分配。
可能有两个异常
- StackOverFlowError
- OutOfMemoryError
程序计数器 PC 寄存器 (线程私有)
Java 虚拟机可以支持多条线程同时执行(可参考《Java 语言规范》第 17 章),每一条 Java 虚拟机线程都有自己的PC(Program Counter)寄存器。在任意时刻,一条Java虚拟机线程 只会执行一个方法的代码,这个正在被线程执行的方法称为该线程的当前方法(Current Method)。如果这个方法不是 native 的,那 PC 寄存器就保存 Java 虚拟机正在执行的 字节码指令的地址,如果该方法是 native 的,那 PC 寄存器的值是 undefined。
名称 | 特征 | 作用 | 配置参数 | 可能异常 |
---|---|---|---|---|
方法区 | 线程共享,生命周期与虚拟机相同,可以不使用连续的内存地址 | 存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据 | OutOfMemoryError | |
运行时常量池 | 方法区的一部分,具有动态性 | 存放字面量及符号引用 | - | OutOfMemoryError |
JAVA 堆 | 线程共享,生命周期与虚拟机相同,可以不使用连续的内存地址 | 保存对象实例,所有对象实例(包括数组)都要在堆上分配 | -Xms -Xsx -Xmn | OutOfMemoryError |
JAVA 虚拟机栈 | 线程私有,生命周期与线程相同,使用连续的内存空间 | Java 方法执行的内存模型,存储局部变量表、操作栈、动态链接、方法出口等信息 | -Xss | OOME、SOE |
本地方法栈 | OOME、SOE | |||
程序计数器 | 线程私有, 生命周期与线程相同 | 大致为字节码行号指示器 | - | - |
栈帧 Frame
栈帧是存储数据和部分过程结果的数据结构,栈帧随着方法的调用而创建,方法的结束而销毁,在一个虚拟机线程中,只有当前正在执行的方法的栈帧是活动,这个栈帧叫做当前栈帧,这个正在执行的方法叫做当前方法。定义这个方法的类叫做当前类。
如果当前方法又调用了另一个方法,此时就不再叫当前方法,其对应的栈帧也不再是当前栈帧了。一个新方法被调用,新的栈帧会随着创建,该方法也就变成了当前方法,新的栈帧也就变成了当前栈帧。当前方法返回时。当前栈帧会将结果传回给前一个栈帧,结果返回后,这个当前栈帧就会销毁,前一个栈帧重新变成当前栈帧。
栈帧是线程本地私有的数据,不可能在一个栈帧之中引用另外一条线程的栈帧。
https://crowhawk.github.io/2017/08/15/jvm_3/
- 生产环境内存溢出如何处理?
- 生产环境服务器该分配多少内存合适?
- 如何对垃圾回收器的性能进行调优?性能指标有哪些?
- 生产环境 cpu 负载飙高如何处理?
- 生产环境该给应用分配多少线程合适?
JVM 参数类型
-
标准参数 所有 JVM 实现均有 如:java -version java -server java -client
-
-X 参数 如:-Xint -Xcomp -Xmixed
-
-XX 非标准化参数
-
boolean 类型 表示启用/禁用参数
-XX:[+]UseG1GC 启用 G1GC
-XX:[-]UseCompressedClassPointers 禁用压缩类空间
-
key - value 类型
-XX:GCTimeRatio=19
-Xms -Xmx -Xss 都是 -XX 参数
-Xms = -XX:InitialHeapSize 最小内存初始化的堆的大小
-Xmx = -XX:MaxHeapSize 最大内存
-Xss = -XX:ThreadStackSize 一个线程堆栈的大小
-
JVM 常见参数
-Xms
-Xmx
-Xss
-XX:NewSize
-XX:MaxNewSize
-XX:NewRatio
-XX:SurvivorRatio
-XX:MatespaceSize
-XX:MaxMatespaceSize
-XX:+UseCompressedClassPointers
-XX:CompressedClassSpaceSize
-XX:InitialCodeCacheSize
-XX:ReservedCodeCacheSize
垃圾收集器分类
- 串行 Serial:Serial、Serial Old
- 并行 Parallel:ParNew(Serial 的多线程版)、Parallel Scavenge、Parallel Old 吞吐量
- 并发 Concurrent:CMS G1 停顿时间
并行 VS 并发
- 并行:指的是多条垃圾收集线程
并行
工作,在进行垃圾回收时,用户的线程是处于等待状态。 - 并发:指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),用户线程在继续运行。而垃圾收集线程运行在另一个CPU上。
吞吐量 VS 停顿时间
- 吞吐量:吞吐量就是 CPU用于运行用户代码的时间 与 CPU总消耗时间 的比值,即 吞吐量 = 运行用户代码时间 /(运行用户代码时间 + 垃圾收集时间)。 假设虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%。
- 停顿时间:垃圾回收器在做垃圾回收时中断应用执行的时间。
并行收集器
启用:-XX:+UseParallelGC -XX:+UseParallelOldGC
并发收集器
启用:
-
CMS:-XX:+UseConcMarkSweepGC -XX:+UseParNewGC
-
G1:-XX:+UseG1GC
查看 JVM 运行是的参数
-XX:+PrintFlagsFinal
-XX:+PrintFlagsInitial
结果中 = 表示默认值 := 表示修改后的值
JDK 常用命令行工具
- jps 类似 Linux 的 ps 只查看 java 进程
- jinfo
- jstat
- jstack
jinfo -flag 参数名 pid
Hotspot UseMaximumCompactionOnSystemGC
Openjdk UseParallelGC