JVM——8.调优工具1(jstat)

2023-11-02

1. 使用背景

一般来说,中大型公司都会有自己的应用监控系统,比如开源的 Zabbix、Open-Falcon、Prometheus等,也可能一些公司自己实现了监控或者告警系统;这些系统可以监控所有在线上的各种应用的运行情况,一旦发生异常(比如CPU利用率过高、FullGC频繁等)会直接通过短信、邮箱或者IM工具发送告警给管理员。

但是对于开发人员来说,并不应该依赖这些监控系统;熟练的使用各种命令行工具,在命令行中就能实现问题的发现定位及解决,是一个优秀工程师的必备技能。

所以我们需要掌握一些简单易用,且高效实用的命令行jvm监控工具,来让我们日常的开发和解决问题更加高效。

2. jstat 的介绍及使用

jstat 就是JDK中自动的一个非常有用的命令,它可以展示出当前运行系统的JVM的Eden、Survivor、老年代等的内存使用情况,还能展示出Young CG、Full GC等的执行情况及耗时。

通过这些指标,我们就可以轻松的分析出当前系统的运行情况、GC情况、内存分配是否合理等。

2.1 jstat -gc PID 介绍

在服务器上执行jps(不懂的自行百度)命令,就可以看到当前服务器中正在运行的java进程,每个进程前面会有一个进程ID,也就是这里的PID。

然后使用这个PID来执行 jstat -gc PID命令:
jstat -gc PID: 只能得到当前系统运行情况的一行指标;
jstat -gc PID 1000 10:每1000毫秒(1秒)执行一次,共执行10次;
jstat -gc PID 1000:每秒执行一次,一直不停的执行;

示例:
1614240513.png

  • S0C:From Survivor区的大小;
  • S1C:To Survivor区的大小;
  • S0U:From Survivor区当前使用的内存大小;
  • S1U:To Survivor区当前使用的内存大小;
  • EC:Eden区的大小;
  • EU:Eden区当前使用的内存大小;
  • OC:老年代的大小;
  • OU:老年代当前使用的内存大小;
  • MC:方法区(永久代、元数据区)的大小;
  • MU:方法区(永久代、元数据区)的当前使用的内存大小;
  • YGC:系统运行迄今为止的Young GC次数;
  • YGCT:Young GC的耗时;
  • FGC:系统运行迄今为止的Full GC次数;
  • FGCT:Full GC的耗时;
  • CGC:Concurrent GC的次数;(JDK11)
  • CGCT:Concurrent GC的耗时;(JDK11)
  • GCT:所有GC的总耗时;

到这里,如果JVM基础好一点的同学,你应该能够发现:根据这些指标,我们就能完成JVM的各项监控了;
这个命令也是 最完整、最常用和最实用的jstat的命令

2.2 其他的 jstat命令

  • jstat -gccapacity PID:堆内存分析;
  • jstat -gcnew PID:年轻代GC分析;(TT和MTT:对象在年轻代存活的年龄和存活的最大年龄)
  • jstat -gcnewcapacity PID:年轻代内存分析;
  • jstat -gcold PID:老年代GC分析;
  • jstat -gcoldcapacity PID:老年代内存分析;
  • jstat -gcmetacapacity PID:元数据区内存分析;

这些命令一看也就知道,是分别针对单独的某个区域进行分析和统计的,可以都自己使用看看。

2.3 jstat -gc PID 使用

首先用一段代码来进行GC的模拟:

/**
 * jvm options:
 *  -Xms20m -Xmx20m -Xmn5m -XX:SurvivorRatio=8 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC
 */
public class JstatDemo {
	public static void main(String[] args) throws InterruptedException {
		TimeUnit.SECONDS.sleep(15); // 便于等待执行jps, jstat命令...
		for (int i = 0; i < 10; i++) {
			call();
		}
	}

	private static void call() throws InterruptedException {
		List<byte[]> list = new ArrayList<>();
		for (int j = 0; j < 4; j++) {
			list.add(new byte[1024 * 1024]);
			TimeUnit.SECONDS.sleep(1);
		}
	}
}

注意代码上面的jvm参数:堆空间20m,新生代5m(按比例Eden区4m),老年代15m。

按照预想,代码的执行情况应该为:
在 call()方法的for循环中,每秒生成1m对象,由于Eden区是4m,则在生成第4m对象的时候,应该发生 Young GC;(则每第4秒发生一次YGC)
并且使用了list引用生成的对象,使得它们此时并不是垃圾对象,不会直接被GC掉;
而Survivor区只有0.5m,肯定是不够存放Young GC后存活的3M对象的,所以此时这3M对象会通过分配担保进入到老年代;

老年代的大小为15m,但是空间分配担保有特定的比较规则(不知道的可以看看前面的文章),所以不会等到老年代真正快要满的时候才发生 Full GC;有可能在使用10m左右,甚至一半的时候就发生Full GC;(发生Full GC时,list都成为了垃圾对象,可以被回收)
理想情况下,假设按照10m发生Full GC计算,,每3秒会有3M对象进入老年代,则在第四次进入老年代的时候,老年代空间不够,发生Full GC;此时应该是第15秒(从第一次进入老年代开始计算,此时新生代中已经消耗了3秒);
后面则应该每12秒,老年代发生一次Full GC;

当然这些都是很理想的情况,实际中会受机器性能,JDK版本等多种因素影响,导致执行情况跟理想情况有差别。

执行情况:
1614245224.png

  • 第3行:代码正式开始执行;
    • 可以看到EU列也就是Eden区是以每秒增大1M的速率进行分配的;
  • 第6行:EU列由 3498变成了1024;
    • 就是上面说的Eden区大小为4m,经过3秒后使用了3m,再继续时放不下了,发生YGC;再看YGC列由0变为了1;
    • 并且OU列由 0变成了3074,则是通过分配担保进入到老年代的3M对象;
  • 第9行:第2个3秒,再次发生YGC,YGC列由1变成了2;
    • OU列由 3074变成了5407;(这里没有按我们预想的进行晋升,预想的这里该又晋升3m,不过不影响jstat的分析)
  • 第18行:第5个3秒,再次发生YGC,YGC列由4变成了5;
    • OU列由6431变成了3346,也就是发生了Full GC,回收了老年代的对象;(这里也没有按我们预想的10m左右才回收,不过不影响jstat的分析)
    • FGC列由0变成了2,这里也是直接就发生了2次 FGC…
  • 第31行:第9个3秒,再次发生FGC,FGC列由2变成了4;

这里也看到了,真正的执行情况跟逻辑分析的情况有不小的出入。

3. 关注指标及计算分析

3.1 关注指标

一个运行在线上的应用系统,对于JVM方面我们需要关注的是它的什么呢?我觉得是以下几类指标:

  1. 对象的增长速率(每秒或者每分钟生成多大内存的对象);
  2. Young GC 的发生频率 和耗时;
  3. 每次 Young GC之后,有多少对象能够存活;
  4. 每次 Young GC之后,有多少对象进入了老年代;
  5. 老年代对象增长的速率;
  6. Full GC 的发生频率 和耗时;

3.2 计算分析

那我们如何运用 jstat这个命令来得到这些指标呢?

  1. 对象的增长速率:
    • 解释:这个指标,也是我们平时对于jvm第一个要了解的东西,也就是随着系统运行,每秒会在新生代的Eden区分配多少对象;
    • 测算:执行jstat -gc PID 1000 10命令;
      • 假设第一秒统计信息里展示Eden区使用了200M,第二秒展示Eden区使用了210M,第三秒展示使用了219M…
      • 以此类推,就可以大概推算出来,当前系统每秒新增 10M 左右的对象;
      • 这里也可以根据自己的系统灵活调整使用,比如系统负载很低,不一定每秒都有请求,就可以把上面的1秒改成1分钟,甚至10分钟等;然后统计系统1分钟或者10分钟大概增长多少对象;
      • 另外就是,系统可能存在高峰期和日常期两种状态,也得分开进行统计;
  2. Young GC 的发生频率 和耗时:
    • 解释:其实在知道了对象的增长速率的情况下,根据你设定的Eden区内存大小,可以很容易推算出来多久发生一次Young GC;
      • 比如你的Eden区大小为800M,然后每秒生成10M的对象,那么就大概在80s左右会发生一次Young GC;
    • 测算:当然我们也可以通过 执行jstat -gc PID 1000命令进行真实测算;
      • 只需要观察 YGC列每多少秒新增1次,也就知道真实多少秒发生一次Young GC了;
      • 对于Young GC的平均耗时:在YGC列的后面一列 YGCT中,展示的是发生了当前次数YGC的总耗时;
        比如系统运行了24小时之后,发生了100次YGC,总耗时YGCT为200ms,则平均下来每次Young GC大概耗时就2毫秒左右;
      • 也就是说你的系统,每次Young GC会导致停顿2毫秒(可以忽略不计);
  3. 每次 Young GC之后,有多少对象能够存活 和进入老年代:
    • 解释:对于每次Young GC后有多少对象能够存活,我们是没法直接看出来的,但是可以进行推算;
      • 之前我们已经测试出来了,大概80s左右发生一次Young GC;
    • 测算:那我们此时执行 jstat -gc PID 80000 10,每80秒执行一次,连续执行10次;
      • 这个时候就可以观测到,每隔80秒发生了一次Young GC后,此时 Eden区、Survivor区、老年代的对象变化;
      • 比如 800M的Eden区,在发生了Young GC之后,正常情况下Eden区肯定对象很少了,比如剩几十M(新生成的);
        但是对于存活的对象,要么进入Survivor区,要么进入老年代;
      • 有多少对象进入老年代:我们可以通过 OU列的大小变化,知道每次Young GC后进入老年代的对象大小;比如有 10M对象进入老年代;
      • 老年代对象增长的速率:每80s发生一次Young GC,有10M对象进入了老年代,也就是说 老年代的对象增长速率为 每80秒10M;
      • 有多少对象存活:并且通过 S1U列的大小变化,知道每次Young GC后进入 Survivor区的对象大小;-> 这样也就能得出每次Young GC之后存活的对象大小(进入老年代 + Survivor区);
      • 另外,老年代的对象是不应该不停的快速增长的,甚至在正常运行情况下,极少的增长;因为普通系统没有那么多长期存活的对象,只要新生代内存分配合理,老年代的对象增长会很少的;
  4. Full GC 的发生频率 和耗时:
    • 解释:跟Young GC的发生频率和耗时一样,在知道了老年代对象的增长速率的情况下,根据你设定的老年代内存大小,也可以很容易推算出多久发生一次Full GC;
      • 比如老年代大小也为800M,然后每80s有10M对象进入老年代,那么就是6400s也就是接近2小时左右发生一次Full GC;
      • 对于Full GC的平均耗时,也一样可以通过FGC列的后面一列 FGCT,也就是发生了当前次数的FGC的总耗时计算出来;
      • 比如一共执行了 10次 FGC,总耗时为2s,也就是说 每次Full GC大概需要耗时 0.2s 左右;

对于这些指标的获取与分析,都可以使用 jstat这个命令实现;并且结合jvm的运行原理,可以比较容易的掌握到线上系统的jvm的运行情况,也可以对于jvm的具体运行情况进行针对性的优化。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JVM——8.调优工具1(jstat) 的相关文章

  • 使用 JDBC 获取 Oracle 11g 的最后插入 ID

    我是使用 Oracle 的新手 所以我将放弃之前已经回答过的内容这个问题 https stackoverflow com questions 3131064 get id of last inserted record in oracle
  • NoInitialContextException:heroku 战争部署

    我一直在开发一个 J2EE 项目 并且在其中使用连接池 也通过部署在 heroku 上的数据库进行访问 我使用以下代码来设置 Connection 对象 Context initContext new InitialContext Cont
  • 获取文件的锁

    我想在对特定文件开始 threo read 时获取文件上的锁定 以便其他应用程序无法读取已锁定的文件并希望在线程终止时释放锁定文件 您可以获得一个FileLock https docs oracle com javase 8 docs ap
  • Android 中的列表(特别是 RecyclerView 和 CardView)如何工作

    请原谅我问这个问题 但我是 Android 开发新手 尽管我正在尝试了解developer android com 网站上的基础知识 但大多数示例 即使他们说它们是为 Android Studio 构建的 尚未设置为使用 Gradle 因此
  • 文本在指定长度后分割,但不要使用 grails 打断单词

    我有一个长字符串 需要将其解析为长度不超过 50 个字符的字符串数组 对我来说 棘手的部分是确保正则表达式找到 50 个字符之前的最后一个空格 以便在字符串之间进行彻底的分隔 因为我不希望单词被切断 public List
  • 从 MS Access 中提取 OLE 对象(Word 文档)

    我有一个 Microsoft Access 数据库 其中包含一个包含 Microsoft Word 文档的 OLE 对象字段 我试图找到代码来检索保存在 OLE 对象中的文件 以便用户可以从我的 JavaFx 应用程序中的按钮下载它 但没有
  • Logback:SizeAndTimeBasedRollingPolicy 不遵守totalSizeCap

    我正在尝试以一种方式管理我的日志记录 一旦达到总累积大小限制或达到最大历史记录限制 我最旧的存档日志文件就会被删除 当使用SizeAndTimeBasedRollingPolicy在 Logback 1 1 7 中 滚动文件追加器将继续创建
  • tomcat 7.0.50 java websocket 实现给出 404 错误

    我正在尝试使用 Java Websocket API 1 0 JSR 356 中指定的带注释端点在 tomcat 7 0 50 上实现 websocket 以下是我如何对其进行编码的简要步骤 1 使用 ServerEndpoint注解编写w
  • FileNotFoundException - Struts2 文件上传

    Strange FileNotFoundException使用Struts2上传文件时 这是 JSP 的一部分
  • Android蓝牙java.io.IOException:bt套接字已关闭,读取返回:-1

    我正在尝试编写一个代码 仅连接到运行 Android 5 0 KitKat 的设备上的 目前 唯一配对的设备 无论我尝试了多少方法 我仍然会收到此错误 这是我尝试过的最后一个代码 它似乎完成了我看到人们报告为成功的所有事情 有人能指出我做错
  • 如何使用正则表达式验证 1-99 范围?

    我需要验证一些用户输入 以确保输入的数字在 1 99 范围内 含 这些必须是整数 Integer 值 允许前面加 0 但可选 有效值 1 01 10 99 09 无效值 0 007 100 10 5 010 到目前为止 我已经制定了以下正则
  • 在 Java 中通过 XSLT 分解 XML

    我需要转换具有嵌套 分层 表单结构的大型 XML 文件
  • 如何通过 Android 按钮单击运行单独的应用程序

    我尝试在 Android 应用程序中添加两个按钮 以从单独的两个应用程序订单系统和库存系统中选择一个应用程序 如图所示 我已将这两个应用程序实现为两个单独的 Android 项目 当我尝试运行此应用程序时 它会出现直到正确选择窗口 但是当按
  • 如何让 Emma 或 Cobertura 与 Maven 一起报告其他模块中源代码的覆盖率?

    我有一个带有 Java 代码的多模块 Maven 设置 我的单元测试在其中一个模块中测试多个模块中的代码 当然 这些模块具有相互依赖性 并且在测试执行之前根据需要编译所有相关模块中的代码 那么 如何获得整个代码库覆盖率的报告 注意 我不是问
  • Hadoop NoSuchMethodError apache.commons.cli

    我在用着hadoop 2 7 2我用 IntelliJ 做了一个 MapReduce 工作 在我的工作中 我正在使用apache commons cli 1 3 1我把库放在罐子里 当我在 Hadoop 集群上使用 MapReduceJob
  • 替换文件中的字符串

    我正在寻找一种方法来替换文件中的字符串而不将整个文件读入内存 通常我会使用 Reader 和 Writer 即如下所示 public static void replace String oldstring String newstring
  • 何时在 hibernate 中使用 DiscriminatorValue 注解

    在 hibernate 中使用 DiscriminatorValue 注释的最佳场景是什么以及何时 这两个链接最能帮助我理解继承概念 http docs oracle com javaee 6 tutorial doc bnbqn html
  • 使用 JFreeChart 为两个系列设置不同的 y 轴

    我正在使用 JFreeChart 使用折线图绘制两个数据系列 XYSeries 复杂的因素是 其中一个数据系列的 y 值通常远高于第二个数据系列的 y 值 假设第一个系列的 y 值约为数百万数量级 而第二个数据系列的 y 值约为数百万数量级
  • 如何使用通配符模拟泛型方法的行为

    我正在使用 EasyMock 3 2 我想基于 Spring Security 为我的部分安全系统编写一个测试 我想嘲笑Authentication http docs spring io autorepo docs spring secu
  • 即使调整大小,如何获得屏幕的精确中间位置

    好的 这个问题有两部分 当我做一个JFrame 并在其上画一些东西 即使我将宽度设置为 400 并使其在一个项目击中它时 当然 允许项目宽度 它会反弹回来 但由于某种原因 它总是偏离屏幕约 10 个像素 有没有办法解决这个问题 或者我只需要

随机推荐

  • 优惠券秒杀(三)

    L1296686146 冗谪 2023 07 28 13 24 发表于陕西 收录于合集 redis7个 优惠券秒杀一人一单 优惠券的目的是为了引流 但是目前的情况是一个人可以无限制的抢这个优惠券 因此 代码中应该添加一个用户只能下一单的逻辑
  • vue 监听浏览器页面关闭_监听浏览器关闭事件的解决方案

    在web项目开发中 关于浏览器关闭事件有两个很常见的问题 为什么我没有监听浏览器关闭事件 我监听到了这个事件 但写在事件里的异步请求为什么发送不成功 原因分析 这两个问题无外乎两个原因 浏览器关闭事件未被触发 和 异步请求发送失败 原因1
  • Centos7 离线安装 nginx

    背景 某些项目处于私有云中 并不能连接互联网 此时安装各种组件都是相当的麻烦 本文记录在某项目中离线安装部署Nginx 首先是安装依赖包 附件地址上传到csdn 免积分下载 nginx 1 21 6及Nginx依赖的rpm安装包 Linux
  • 华为数字化转型之道 平台篇 第十一章 统一的数据底座

    平台篇 第十一章 统一的数据底座 在从传统信息化向数字化转型的过程中 企业积累了海量数据 并且还在爆发式增长 数据很多 但真正能产生价值的数据却很少 数据普遍存在分三年 不拉通的问题 缺乏统一的定义和架构 找到想要的 能用的数据越来越难 如
  • C语言结构体与共用体(1)

    结构体 有时需要将不同类型的数据组合成一个人有机的整体 以便于引用 如 一个学生有学号 姓名 性别 年龄 地址等属性 int num char name 20 char sex int age char addr 30 定义一个结构体的一般
  • php和tp5学习笔记

    之前看过php的基础教学视频 了解了一下 自己没有上手实践 现在为了项目需要 需要扎实学习一下 所以做一下笔记吧 php学习笔记 1 环境 1 用phpstudy搭建php环境 2 搭建thinkphp环境 2 知识点 2 1 URL 2
  • python判断一个集合是否为另一个集合的子集

    a 1 2 3 4 b set 1 2 b issubset a
  • FusionAD:用于自动驾驶预测和规划任务的多模态融合

    论文背景 自动驾驶 AD 任务通常分为感知 预测和规划 在传统范式中 AD中的每个学习模块分别使用自己的主干 独立地学习任务 以前 基于端到端学习的方法通常基于透视视图相机和激光雷达信息直接输出控制命令或轨迹 论文提出了基于BEV多模态的多
  • new JSONObject(true)

    构造容量为16个单位的HashMap作为JSONObject JSONObject jsonObject new JSONObject 构造容量为16个单位JSONObject 可选择器容器是否有序 LinkedHashMap true H
  • Mysql GROUP_CONCAT(expr) 介绍

    1 MYSQL版本 Welcome to the MySQL monitor Commands end with or g Your MySQL connection id is 86693 Server version 5 7 23 My
  • TCP和UDP的最完整的区别

    转自 https blog csdn net li ning article details 52117463 TCP UDPTCP与UDP基本区别 1 基于连接与无连接 2 TCP要求系统资源较多 UDP较少 3 UDP程序结构较简单 4
  • 防逆向技术简史:从代码混淆到虚拟机保护技术

    那么如何才能保护自己开发APP不被逆向破解呢 在道高一尺魔高一丈的网络安全攻防对抗中 防逆向保护技术也在不停更新演进 我在这里梳理了几个关键时期的防逆向保护技术 让大家对APP防护有一个更好理解 启蒙阶段 防逆向保护始于代码混淆技术 这个时
  • git使用——merge、rebase 区别和 idea 里的 Merge into current、Rebase current onto seleced 使用

    文章目录 1 Git 中 merge 和 rebase 的区别 2 在Intellij idea 中的使用 1 Git 中 merge 和 rebase 的区别 简介 git pull rebase和 git pull区别 是git fet
  • 【记录】mmsegmentation 训练自己的数据集

    文章目录 数据集标注工具选择 labeme 标注 精灵标注助手 photoshop标注 标准格式转换 转化为COCO格式 转化为VOC格式 json文件 mask图像 划分训练集和测试集 修改配置文件 修改data root为自己的路径 搜
  • vue:vue的计算属性以及监听属性(三)

    1 计算属性 相较于函数而言计算属性更加智能 他是基于它们的响应式依赖进行缓存的 也就是说只要相关依赖 下述例子中的area 没有发生改变 那么这个计算属性的函数不会重新执行 而是直接返回之前的值 这个缓存功能让计算属性访问更加高效 只要修
  • 解决STM32进行IAP升级跳转至APP后无法进中断问题

    最近碰到STM32在进行IAP升级时跳转至APP后无法进入中断 导致程序无法运行 正常情况下STM32进行IAP升级时 在跳转至APP程序之前 应该关闭外设 关闭中断 BootLoader中跳转程序如下 typedef void pFunc
  • Centos7 使用nginx 和 supervisord 部署python +tornado+nodejs

    一 我用到的工具是netty winscp linux 环境下安装nginx步骤 开始前 请确认gcc g 开发类库是否装好 默认已经安装 yum y install gcc gcc c 安装gcc和c 编译器 1 centOS安装依赖 y
  • vue动态判断input readonly属性

    1 如果isReadOnly等于true时 input只读 否则可以编辑
  • chatgpt赋能python:Python怎么求平均值?全面解析平均值计算方法

    Python怎么求平均值 全面解析平均值计算方法 作为一种简洁易用的计算机编程语言 Python的应用范围已经越来越广泛 在Python中 我们可以轻松地进行各种统计和计算工作 其中求平均值是最常见的计算之一 在本文中 我们将全面解析Pyt
  • JVM——8.调优工具1(jstat)

    文章目录 1 使用背景 2 jstat 的介绍及使用 2 1 jstat gc PID 介绍 2 2 其他的 jstat命令 2 3 jstat gc PID 使用 3 关注指标及计算分析 3 1 关注指标 3 2 计算分析 1 使用背景