目录
一、 JVM概述
二、 JMM 虚拟机内存模型
1. 程序计数器 (PC 寄存器)
2. 虚拟机栈 & 本地方法栈
3. 堆
4. 方法区
5. 永久代
6. 元空间
7. 直接内存
三、JVM 垃圾回收算法
1. 什么是垃圾回收?
2. 可触及性
3. 四种引用级别
4. 面试题:
5.主要垃圾回收算法
四、JVM 垃圾收集器
1.串行回收器 - Serial
2.并行回收器 - ParNew & ParallelGC & ParallelOldGC
3.并行回收器 - CMS (Concurrent Mark Sweep)
4.G1 (Garbage First Garbage Collector)
五、JVM 常用参数
六、JVM 监控优化
一、 JVM概述
同一个 Java 程序,被编译为一组 Java 字节码 的集合之后,就可以通过 Java虚拟机 运行于不同的操作系统上,它以 Java 虚拟机为中介,实现跨平台的特性。
① 类主动加载有?种?式??
1> Student student = new Student() 直接 new 一个对象;
2> 利?反射、clone;
3> 初始化?类的时候,?类会被初始化;
4> 调??个类的静态?法
② 类加载的五个步骤:
③ 什么是符号引??什么是直接引??
-
符号引? 以?组 符号来描述 引用的?标直接的关系。 -
直接引? 通过 Class ?件加载到内存之后,通过对符号引?的转换,就有了对应的直接引?。 -
为什么有符号引?? java 类被编译成 Class?件,并不知道具体的引?地址,所以就以符号引?代替。 在解析阶段,把符号引?转换为了真正的地址,也就是转换为直接引?。
二、 JMM 虚拟机内存模型
1. 程序计数器 (PC 寄存器)
1> 是 当前线程 所执行的 字节码的行号指示器,指向虚拟机字节码指令的位置;
2> 被分配了一块 较小的内存空间;
3> 针对于 非Native方法:是当前线程执行的字节码的行号指示器;
? 针对于 Native方法:则为undefined;
4> 每个线程都有自己独立的程序计数器,所以该内存是 线程私有 的;
5> 这块区域是 唯一 一个在虚拟机中 没有规定任何OutOfMemoryError 情况的区域
2. 虚拟机栈 & 本地方法栈
1> 虚拟机栈为执行 Java 方法服务,是描述方法执行的 内存模型;
2> 本地方法栈贼是为本地方法服务的;
3> 栈是 线程私有 的内存空间;
4> 在栈中保存的主要内容为 栈帧。它的 数据结构 就是 先进后出。每当函数被调用,该函数就会被 入栈,每当函数执行完毕,就会执行 出栈 操作。而当前 栈顶,即为正在执行的函数;
栈帧与?法调?相对应:
?栈 ——> ?法调?
出栈 ——> 结果返回
栈顶 ——> 当前正在执?的?法
5> 每个方法在执行的同时都会创建一个 栈帧 用于存储 局部变量表、操作数栈、帧数据区、动态链接、方法出口 等信息。
实战:
(1) 栈的??决定?法调?深度:(通过 count 表示调用深度)
public class StackOverflowTest {
// 为了记录方法调用层级数的
private static int count=0;
public static void main(String[] args) {
try {
while (true) {
count();
}
} catch (Throwable e) {
System.out.println("count = " + count);
throw e;
}
}
private static void count() {
count++;
count();
}
}
① 设置最?栈内存为 -Xss160K
?② 设置最?栈内存为 -Xss256K
(2) 栈帧中的 局部变量表 演示:说明 方法入参、局部变量 是存在栈中的(栈空间越大,存的入参和变量越多,层次也能越深)
public class StackOverflow2Test {
private static int count=0;
public static void main(String[] args) {
try {
while (true) {
count(1L, 2L, 3L, 4L, 5L);
}
} catch (Throwable e) {
System.out.println("count = " + count);
throw e;
}
}
private static void count(long arg1, long arg2, long arg3, long arg4, long arg5) {
long num1 = 1;
long num2 = 2;
long num3 = 3;
long num4 = 4;
long num5 = 5;
long num6 = 6;
long num7 = 7;
long num8 = 8;
count++;
count(arg1, arg2, arg3, arg4, arg5);
}
}
?① 设置最?栈内存为 -Xss160K
?② 设置最?栈内存为 -Xss256K
?
3. 堆
1> 运行时数据区,几乎所有的对象都保存在 java 堆中;
2> Java 堆是 完全自动化管理 的,通过垃圾回收机制,垃圾对象会被自动清理,不需要显式地释放;
3> 堆是垃圾收集器 进行GC的最重要内存区域;
4> Java 堆可以分为:新生代(Eden区、S0区、S1区)和 老年代;
5> 在绝大多数情况下,对象 首先分配在 eden 区,在 一次新生代 GC回收后,如果对象还存活,则会进入S0或S1。之后每经历过一次新生代回收,对象如果存活,它的年龄就会加一。当对象的 年龄达到一定条件后,就会被认为是老年代对象,从而进入老年代。
4. 方法区
1> 逻辑上的东西,是 JVM的规范,是所有虚拟机必须遵守的;
2> 是 JVM 所有 线程共享 的、用于 存储类信息,例如:类的字段、方法数据、常量池等;
3> 方法区的大小决定了系统可以保存多少个类;
4> JDK8之前——永久代
? ????JDK8及之后——元空间
5. 永久代
指内存的 永久保存区域,主要存放 Class 和 Meta(元数据)的信息,Class 在被加载的时候放入永久区域,它和存放实例的区域不同,GC不会在主程序运行期对永久区域进行清理。所以这也导致了永久代的区域会随着加载的 Class 的增多而胀满,最终抛出OOM异常。
如果系统使用了一些 动态代理,那么有可能会在运行时生成大量的类,从而造成内存溢出。所以,设置合适的永久代大小,对于系统的 稳定性 是至关重要。
-XX:PermSize
? ????????设置初始永久代大小。例如:-XX:PermSize=5MB
-XX:MaxPermSize
? ????????设置最大永久代大小,默认情况下为64MB。例如:-XX:MaxPermSize=5MB
6. 元空间
1> 在 Java8 中,永久代已经被移除,被一个称为 “元数据区”(元空间)的区域所取代;
2> 元空间的本质和永久代类似,元空间与永久代之间 最大的区别 在于:元空间并不在虚拟机中,而是使用堆外的直接内存;
3> 因此与永久代不同,如果不指定大小,默认情况下,虚拟机会耗尽所有的可用系统内存;
-XX:MaxMetaspaceSize
? ????????设置最大元数据空间。例如:-XX:MaxMetaspaceSize=20MB
为什么使用元空间替换永久代?
1> 避免OOM的异常。因为元空间使?的是堆外内存,默认情况下,虚拟机会耗尽所有的可?系统内存;
2> JRockit 没有永久代,也不需要运维?员设置??,运?的还不错,有性能的损耗,但是不?。性能的部分损耗来换取了更?的安全保障。Oracle 可能会将 HotSpot 和 JRockit 合?为?。
7. 直接内存
跟 NIO 息息相关的;
堆外内存,直接向系统申请内存空间;
与 Java 堆相?,读写访问有?定的优势。但是在内存空间申请上,速度偏低;
应?场景:内存空间申请次数少,并且访问较为频繁的情况。
三、JVM 垃圾回收算法
1. 什么是垃圾回收?
GC:垃圾回收,即:Garbage Collection。
垃圾:特指存在于 内存 中的、不会再被使用的对象。
回收:清除内存中的“垃圾”对象。
2. 可触及性
① 什么是可触及性?
就是 GC 时,是根据它来确定对象是否可被回收。
也就是说,从根节点开始是否可以访问到某个对象,就说明这个对象是否被使用。
② 可触及性分为3种状态:
可触及:从根节点开始,可以到达某个对象;
可复活:对象引用被释放,但是可能在 finalize() 函数中被初始化复活;
不可触及:由于 finalize() 只会执行一次,所以错过这一次复活机会的对象,则为不可触及状态。
例:
public class DieAliveObject {
private static DieAliveObject dieAliveObject;
public static void main(String[] args) {
dieAliveObject = new DieAliveObject(); // 可触及状态
for (int i = 0; i <= 1; i++) { // 两次循环
System.out.println(String.format("----------GC nums=%d----------", i));
dieAliveObject = null; // 将dieAliveObject对象置为"垃圾对象"
System.gc(); // 通知JVM可以执行GC了
try {
Thread.sleep(100); // 等待GC执行
} catch (InterruptedException e) {
e.printStackTrace();
}
if (dieAliveObject == null) {
System.out.println("dieAliveObject is null");
} else {
System.out.println("dieAliveObject is not null");
}
}
}
/**
* finalize只会被调用一次,给对象唯一一次重生的机会
*/
@Override
protected void finalize() {
System.out.println("finalize is called!");
dieAliveObject = this; // 使对象复生,添加引用
}
}
3. 四种引用级别
① 强引用:
就是一般程序中的引用,例如 Student student = new Student();
② 软引用(java.lang.ref.SoftReferenct):
当 堆空间不足时,才会被回收 。因此,软引用对象不会引起内存溢出。
③ 弱引用(java.lang.ref.WeakReferenct):
当 GC 的时候,只要发现存在弱引用,无论系统堆空间是否不足,均会将其回收。
④ 虚引用:
如果对象持有虚引用,其实与没有引用是一样的。虚引用必须和引用队列在一起使用,它的作用是用于跟踪 GC 回收过程,所以可以将一些资源释放操作放置在虚引用中执行和记录。
?
例:
软引用
内存空间充足的时候执行第一次 GC ,teacher对象不会被回收;创建一个大对象造成空间紧张后,执行第二次 GC ,teacher软引用对象被回收。
public class SoftReferenceDemo {
public static void main(String[] args) throws Throwable{
/** 查看空余内存 */
System.out.println("---------Free " + Runtime.getRuntime().freeMemory() / 1000000 + "M----------");
/** 创建Teacher对象的软引用 */
Teacher teacher = new Teacher("aaa", 15);
SoftReference softReference = new SoftReference(teacher);
System.out.println("softReference=" + softReference.get());
/** 使得teacher失去引用,可被GC回收 */
teacher = null;
/** 执行第一次GC后,软引用并未被回收 */
System.gc();
System.out.println("---------First GC----------");
System.out.println("softReference=" + softReference.get());
/** 可以通过对数组大小数值调整,来造成内存资源紧张 */
byte[] bytes = new byte[7*971*1024];
System.out.println("---------Assign Big Object----------");
/** 执行第二次GC,由于堆空间不足,所以软引用已经被回收 */
System.gc();
System.out.println("---------Second GC----------");
Thread.sleep(1000); // 睡眠1秒钟,保证GC已经执行完毕
System.out.println("softReference=" + softReference.get());
}
}
?
弱引用
创建 teacher 弱引用对象,执行 GC前为被回收 ,执行 GC 后 teacher 弱引用对象被回收。
public class WeakReferenceDemo {
public static void main(String[] args) throws Throwable{
/** 创建Teacher对象的弱引用 */
Teacher teacher = new Teacher("aaa", 15);
WeakReference<Object> weakReference = new WeakReference<>(teacher); /** 创建弱引用对象 */
/** 使得teacher失去引用,可被GC回收 */
teacher = null;
/** 执行GC前,查看弱引用并未被回收 */
System.out.println("---------Before GC----------");
System.out.println("weakReference=" + weakReference.get());
/** 执行GC,所以弱引用已经被回收 */
System.gc();
System.out.println("---------After GC----------");
Thread.sleep(1000); // 睡眠1秒钟,保证GC已经执行完毕
System.out.println("weakReference=" + weakReference.get());
}
}
?
虚引用
虚引用必须和引用队列一起使用。因为垃圾回收时,如果发现它还有虚引用,就会在回收对象后,将这个虚引用加入到引用队列。通过判断是否能从虚引用队列中获取到对象,从而判断是否回收成功。
public class PhantomReferenceDemo {
private static PhantomReferenceDemo obj;
public static void main(String[] args) {
/** 创建引用队列 */
ReferenceQueue<PhantomReferenceDemo> phantomRefQueue = new ReferenceQueue<>();
/** 创建虚引用 */
obj = new PhantomReferenceDemo();
PhantomReference<PhantomReferenceDemo> phantomReference = new PhantomReference<>(obj, phantomRefQueue);
System.out.println("phantomReference = " + phantomReference.get()); // 总会返回null
/** 创建后台线程 */
Thread thread = new CheckRefQueueThread(phantomRefQueue);
thread.setDaemon(true);
thread.start();
/** 执行两次GC,一次被finalize复活,一次真正被回收 */
for (int i = 1; i <=2 ; i++) {
gc(i);
}
}
private static void gc(int nums) {
obj = null;
System.gc();
System.out.println("---------第" + nums + "次GC----------");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (obj == null) {
System.out.println("obj is null");
} else {
System.out.println("obj is not null");
}
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("finalize() is called!");
obj = this; // 复活对象
}
}
/**
* 从引用队列中获得被回收的对象
*/
class CheckRefQueueThread extends Thread {
private ReferenceQueue<PhantomReferenceDemo> phantomRefQueue;
public CheckRefQueueThread(ReferenceQueue<PhantomReferenceDemo> phantomRefQueue ) {
this.phantomRefQueue = phantomRefQueue;
}
@Override
public void run() {
while (true) {
if (phantomRefQueue != null) {
// 将虚引用对象置空,满足GC
PhantomReference<PhantomReferenceDemo> phantomReference = null;
try {
/**
* 当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象后,将这个虚引用加入到引用队列
* ,以通知应用程序对象的回收情况
*
*/
// 从虚引用队列中获取虚引用对象
phantomReference = (PhantomReference<PhantomReferenceDemo>) phantomRefQueue.remove();
} catch (Throwable e) {
e.printStackTrace();
}
// 能从队列中获取到对象说明已经被回收
if (phantomReference != null) {
System.out.println("Object is delete by GC");
}
}
}
}
}
4. 面试题:
1、什么是槽位复用?
本地变量表中的,变量的槽位被占用。例如下面的方法 Gc4(),变量 a 的槽位序号为1,变量 b 的槽位也为1,所以 a 变量会被回收。
对象空间分配:① 栈上分配;不满足栈上分配条件的话 ② TLAB 分配;不满足 TLAB 分配条件的话 ③ (堆上分配)新生代 -> 老年代。引出以下问题:
2、什么是栈上分配?如何可以触发?
-
他的空间?较?。?的对象就不适合在栈上分配了。 -
开启条件,需要同时满?以下三点:
开启 逃逸分析
开启 标量替换
server 模式下
-
逃逸分析
就是判断对象是不是线程私有。
-
标量替换
什么叫做标量?
?? ? ? 不可被进?步分解的量。int,long,byte
什么叫聚合量?
?? ? ? 可被进?步分解的量。例如我们创建的对象。
把聚合量分解为标量。把对应的成员变量存储在栈帧或寄存器上。
例:
/**
* 栈上分配(默认开启)
* 【开启栈上分配】-Xmx50m -Xms50m -XX:+PrintFlagsFinal -XX:+PrintGCDetails -XX:-UseTLAB
* 【关闭栈上分配】-Xmx50m -Xms50m -XX:+PrintFlagsFinal -XX:+PrintGCDetails -XX:-UseTLAB -XX:-DoEscapeAnalysis
* 注意:-XX:+PrintFlagsFinal只是为了查看参数设置情况,可以去掉。
* -XX:-DoEscapeAnalysis (关闭逃逸分析,条件不足,相当于关闭栈上分配)
**/
public class AssignOnStack {
public static void main(String[] args) {
sizeOfStudent();
StopWatch stopWatch = StopWatch.createStarted();
// 制造将近7.5个G左右的对象
for (int i=0; i< 100000000; i++) {
initStudent();
}
stopWatch.stop();
System.out.println("========执行一共耗时:" + stopWatch.getTime(TimeUnit.MILLISECONDS) + "毫秒");
}
/**
* student所占用空间为72bytes
*/
public static void sizeOfStudent() {
Student student = new Student();
student.setName("muse");
System.out.println("========student大小为:" + ObjectSizeCalculator.getObjectSize(student));
System.out.println("========student大小为:" + RamUsageEstimator.humanSizeOf(student));
}
public static void initStudent() {
Student student = new Student();
student.setName("muse");
}
}
默认开启栈上分配:
?关闭栈上分配,耗时明显不同:
?
3、什么是TLAB?好处是什么?
TLAB (Thread Local Allocation Buffer,线程本地分配缓冲区)是 Java 中内存分配的一个概念,它是在 Java 堆中划分出来的针对每个线程的内存区域,专门在该区域为该线程创建的对象分配内存。它的主要目的是在多线程并发环境下需要进行内存分配的时候,减少线程之间对于内存分配区域的竞争,加速内存分配的速度。
参考:什么是 Java 中的TLAB ?_hfer的博客-CSDN博客_tlab
?
5.主要垃圾回收算法
引用计数法、标记清除法、复制算法、标记压缩算法、分代算法、分区算法
对于一个对象A,只要有任何一个对象引用了A,则A的引用计数器就加1,当引用失效时,引用计数器就减1。当对象A的引用计数器的值为0,则对象A不可用。
但引用计数器有两个严重问题:
(1)无法处理 循环引用 的情况。两个对象相互引用的话,计数总不为0,无法回收。
(2)引用计数器要求在每次因引用产生和消除的时候,需要伴随一个加减法操作,对 系统性能 会有一定的影响。
* 因此:JVM并未选择引用计数法作为垃圾回收算法。
标记清除算法是 现代垃圾回收算法的思想基础。
分为两个阶段:① 标记阶段 和 ② 清除阶段。
标记清除算法产生 最大的问题 就是清除之后的 空间碎片。
第一阶段标记:从根节点向下寻找标记有用对象;第二阶段清除:清除标记出来的垃圾对象(上图黑色部分)。
问题:从图可以看到,垃圾回收后,垃圾对象原来的空间成为了空间碎片。
将原有内存空间分为两块。每次只使用其中一块内存,例如:A内存,GC 时将存活的对象复制到B内存中。然后清除掉A内存所有对象。开始使用B内存。
复制算法没有内存碎片,并且如果垃圾对象很多,那么这种算法效率很高。但是它的 缺点是系统内存只能使用1/2。
复制算法在 JVM 中的使用:
因为新生代大多对象都是“朝不保夕”,所以在新生代串行GC中,使用了复制算法。(新生代中 Eden:From:to = 8:1:1,所以在 JVM中浪费的空间就相当于1/10,减少了空间的浪费)
*对象在什么情况下会进入老年代?
-
BigObject -
OldObject -
Survivor to 空间不足
标记压缩算法是一种 老年代的回收算法。
分为两个阶段:① 标记阶段 和 ②压缩阶段。它首先标记存活的对象,然后将所有存活的对象 压缩到内存的一端,然后在清理所有存活对象之外的空间。
该算法 不会产生内存碎片,并且也 不用将内存一分为二。因此其 性价比较高。
?
将堆空间划分为 新生代 和 老年代,根据它们直接的不同特点,执行不同的回收算法,提升回收效率。
?
将堆空间划分成连续的不同 小区间,每个区间独立使用、回收。由于当堆空间大时,一次GC的时间会非常耗时,那么可以控制每次回收多少个小区间,而不是整个堆空间,从而减少一次GC所产生的停顿。
?
四、JVM 垃圾收集器
1.串行回收器 - Serial
串行回收器的 特点: (STW :stop the world)
1>只使用 单线程 进行GC
2>独占式 的GC
串行收集器是 JVM Client 模式 下 默认 的垃圾收集器
JVM 参数 | 作用 |
---|
-XX:SurvivorRatio | 设置eden区与survivor区比例 | -XX:PretenureSizeThreshold | 设置大对象直接进入老年代的阈值 | -XX:MaxTenuringThreshold | 设置对象进入老年代的年龄阈值 |
2.并行回收器 - ParNew & ParallelGC & ParallelOldGC
将串行回收器 多线程化。
与串行回收器有 相同的回收策略、算法、参数。
启动指定收集器:
JVM 参数 | 新生代 | 老年代 |
---|
-XX:+UseSerialGC | 串行回收器 | 串行回收器 | -XX:+UseParNewGC | ParNew | 串行回收器 | -XX:+UseConcMarkSweepGC | ParNew | CMS | -XX:+UseParallelGC | ParallelGC | 串行回收器 | -XX:+UseParallelOldGC | ParallelGC | ParallelOldGC |
?
3.并行回收器 - CMS (Concurrent Mark Sweep)
4.G1 (Garbage First Garbage Collector)
(jdk1.7 开始,目的是取代 CMS,特点是:分区)
G1 全称 Garbage First Garbage Collector。优先回收垃圾比例最高的区域。
G1收集器将堆 划分为多个区域,每次收集部分区域来 减少GC产生的停顿时间。
G1 的 GC 过程:
?
-
第三阶段:混合收集
JVM参数 | 作用 |
---|
-XX:+UseG1GC | 打开G1收集器开关 | -XX:MaxGCPauseMillis | 指定目标最大停顿时间 | -XX:PartallelGCThreads | 设置并发线程数量 | -XX:InitiatingHeapOccupancyPercent | 指定堆的使用率,触发并发标记周期(默认45) |
?
五、JVM 常用参数
执行语法:
Java [-options] [package+className] [arg1,arg2,…,argN]
options:
? -Xms128m ????????????????????????设置初始化堆内存为128M
? -Xmx512m ????????????????????????设置最大堆内存为512M
? -Xmn160m ????????????????????????设置新生代大小为-Xmn160M(堆空间1/4~1/3)
? -Xss128m? ? ? ? ? ? ? ? ? ? ? ? ? ?设置最大栈内存为128M
? -XX:SurvivorRatio? ? ? ? ? ? ? ?设置新生代eden区与from/to空间的比例关系
? -XX:PermSize=64M ???????????设置初始永久区64M
? -XX:MaxPermSize=128M? ? 设置最大永久区128M
? -XX:MaxMetaspaceSize? ? ?设置元数据区大小(JDK1.8 取代永久区)
? -XX:+DoEscapeAnalysis? ? ?启用逃逸分析(Server模式)
? -XX:+EliminateAllocations? ?开启标量替换(默认开启)
? -XX:+TraceClassLoading? ? ?跟踪类的加载
? -XX:+TraceClassUnloading 跟踪类的卸载
? -Xloggc:gc.log? ? ? ? ? ? ? ? ? ? ? 将gc日志信息打印到gc.log文件中
? -XX:+PrintGC? ? ? ? ? ? ? ? ? ? ? ?打印GC日志
? -XX:+PrintGCDetails? ? ? ? ? ? 打印GC详细日志
? -XX:+PrintGCTimeStamps ??输出GC发生的时间
? -XX:+PrintGCApplicationStoppedTime ???????????????GC产生停顿的时间
? -XX:+PrintGCApplicationConcurrentTime? ? ? ? ? ? 应用执行的时间
? -XX:+PrintHeapAtGC? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?在GC发生前后,打印堆栈日志
? -XX:+PrintReferenceGC? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?打印对象引用信息
? -XX:+PrintVMOptions???????????????????????????????????????????打印虚拟机参数
? -XX:+PrintCommandLineFlags ???????????????????????????打印虚拟机显式和隐式参数
? -XX:+PrintFlagsFinal? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 打印所有系统参数
? -XX:+PrintTLAB? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 打印TLAB相关分配信息
? -XX:+UseTLAB? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???打开TLAB
? -XX:TLABSize? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 设置TLAB大小
? -XX:+ResizeTLAB ???????????????????????????????????????????????自动调整TLAB大小
? -XX:+DisableExplicitGC ??????????????????????????????????????禁用显式GC (System.gc())
? -XX:+ExplicitGCInvokesConcurrent ????????????????????使用并发方式处理显式GC
六、JVM 监控优化
能够实时显示系统中各个进程的资源占用情况。
分为两部分:系统统计信息 & 进程信息。
?
系统统计信息:
Line1:任务队列信息,从左到右依次表示:系统当前时间、系统运行时间、当前登录用户数。Load average表示系统的平均负载,即任务队列的平均长度——1分钟、5分钟、15分钟到现在的平均值。
Line2:进程统计信息,分别是:正在运行进程数、睡眠进程数、停止的进程数、僵尸进程数。
Line3:CPU统计信息,us表示用户空间CPU占用率、sy表示内核空间CPU占用率、ni表示用户进程空间改变过优先级的进程CPU占用率。id表示空闲CPU占用率、wa表示待输入输出的CPU时间百分比、hi表示硬件中断请求、si表示软件中断请求。
Line4:内存统计信息,从左到右依次表示:物理内存总量、已使用的物理内存、空闲物理内存、内核缓冲使用量。
Line5:从左到右表示:交换区总量、已使用交换区大小、空闲交换区大小、缓冲交换区大小。
进程信息:
PID:进程id
USER:进程所有者
PR:优先级
NI:nice值,负值->高优先级,正值->低优先级
VIRT:进程使用虚拟内存总量 VIRT=SWAP+RES
RES:进程使用并未被换出的内存。CODE+DATA
SHR:共享内存大小
S:进程状态。 D=不可中断的睡眠状态 R=运行
S=睡眠 T=跟踪/停止 Z=僵尸进程
%CPU:上次更新到现在的CPU时间占用百分比
%MEM:进程使用的物理内存百分比
TIME+:进程使用的CPU时间总计,单位 1/100秒
COMMAND:命令行
性能监测工具,显示单位均为kb。它可以统计CPU、内存使用情况、swap使用情况等信息,也可 以指定采样周期和采用次数。
例如:每秒采样一次,共计3次。 vmstat 1 3
procs列:r表示等待运行的进程数。b表示处于非中断睡眠状态的进程数。
memory列:swpd表示虚拟内存使用情况。free表示空闲内存量。buff表示被用来作为缓存的内存。
swap列:si表示从磁盘交换到内存的交换页数量。so表示从内存交换到磁盘的交换页数量。
io列:bi表示发送到块设备的块数,单位:块/秒。bo表示从块设备接收到的块数。
system列:in表示每秒的中断数,包括时钟中断。cs表示每秒的上下文切换次数。
cpu列:us表示用户cpu使用时间。sy表示内核cpu系统使用时间。id表示空闲时间。 wa表示等待io时间。
?
可以提供详尽的I/O信息。
如果只看磁盘信息,可以使用-d参数。即:Iostat –d 1 1 (每1秒采集一次持续1次)
?
tps列 表示该设备每秒的传输次数。
Blk_read/s列 表示每秒读取块数。
Blk_wrtn/s列 表示每秒写入块数。
Blk_read列 表示读取块数总量。
Blk_wrtn列 表示写入块数总量。
用于列出 Java 的进程。
执行语法:
jps [-options]
jps 列出java进程id和类名
? ????????例:91275 FireIOTest
jps –q 仅列出java进程id
? ????????例:91275
jps –m 输出java进程的入参
? ????????例:91730 FireIOTest a b
jps –l 输出主函数的完整路径
? ????????例:91730 day1.FireIOTest
jps –v 显示传递给JVM的参数
? ????????例:91730 FireIOTest -Xmx512m -XX:+PrintGC -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=51673:/Applications/IntelliJIDEA.app/Contents/bin - Dfile.encoding=UTF-8
用于查看堆中的运行信息。
执行语法:jstat –help jstat -options
jstat <-option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
jstat -class -t 73608 1000 5
? ????????查看进程73608的ClassLoader相关信息,每1000毫秒打印1次,一共打印5次,并输出程序启动到此刻的Timestamp数。
jstat -compiler -t 73608? ? ? ? ? ?查看指定进程的编译信息。
jstat -gc 73608 ????????????????????????查看指定进程的堆信息。
jstat -gccapacity 73608? ? ? ? ? ?查看指定进程中每个代的容量与使用情况
jstat -gccause 73608 ??????????????显示最近一次gc信息
jstat -gcmetacapacity 73608? ?查看指定进程的元空间使用信息
jstat -gcnew 73608? ? ? ? ? ? ? ? ? ?查看指定进程的新生代使用信息
jstat -gcnewcapacity 73608? ? ?查看指定进程的新生代各区大小信息
jstat -gcold 73608? ? ? ? ? ? ? ? ? ? ?查看指定进程的老年代使用信息
jstat -gcoldcapacity 73608? ? ? ?查看指定进程的老年代各区大小信息
jstat -gcutil 73608? ? ? ? ? ? ? ? ? ? ?查看指定进程的GC回收信息
jstat -printcompilation 73608? 查看指定进程的JIT编译方法统计信息
用于查看运行中java进程的 虚拟机参数。
执行语法:
jinfo [option] <pid>
jinfo -flag MaxTenuringThreshold 73608
? ????????查看进程73608的虚拟机参数MaxTenuringThreshold 的值
jinfo -flag +PrintGCDetails 73608
? ????????动态添加进程73608的虚拟机参数+PrintGCDetails,开启GC日志打印。
jinfo -flag -PrintGCDetails 73608
? ????????动态添加进程73608的虚拟机参数+PrintGCDetails,关闭GC日志打印。
命令用于生成指定java进程的dump文件;可以查看堆内对象实例的统计信息,查看ClassLoader信息和finalizer队列信息。
执行语法:
jmap [option] <pid>
jmap -histo 73608 > /Users/aaa/a.txt
????????? 输出进程73608的实例个数与合计到文件a.txt中
jmap -dump:format=b,file=/Users/aaa/b.hprof 73608
? ????????输出进程73608的堆快照,可使用jhat、visual VM等进行分析
命令用于分析jmap生成的堆快照。
执行语法:
jhat [-stack <bool>] [-refs <bool>] [-port <port>] [-baseline <file>] [-debug <int>] [- version] [-h|-help] <file>
jhat b.hprof
? ????????分析jmap生成的堆快照b.hprof,通过 http://127.0.0.1:7000 这个地址查看。 OQL(Object Query Language)
命令用于导出指定java进程的堆栈信息。
执行语法:
jstack [-l] <pid>
jstack -l 73608 > /Users/muse/d.txt 输出进程73608的实例个数与合计到文件a.txt中
查看文件:
命令用于导出指定java进程的堆栈信息,查看进程,GC等。
执行语法:
jcmd <pid | main class> <command ...|PerfCounter.print|-f file>
jcmd -l ????????????????????????列出java进程列表
jcmd 26586 help ????????输出进程java进程为26586所支持的jcmd指令
jcmd 26586 VM.uptime? ? ? ? ? ? 查看java进程启动时间
jcmd 26586 Thread.print ????????打印线程栈信息
jcmd 26586 GC.class_histogram ????????查看系统中类的统计信息
jcmd 26586 GC.heap_dump /Users/muse/a.txt ????????导出堆信息
jcmd 26586 VM.system_properties ????????获得系统的Properties内容
jcmd 26586 VM.flags ????????????????????????获得启动参数
jcmd 26586 PerfCounter.print? ? ? ? ? ?获得性能统计相关数据
?*此笔记是对 爪哇-缪斯 老师 JVM 课程笔记的整理。
|