disgare 的博客
首页
博客
分类
标签
首页
博客
分类
标签
  • 网络

    • 计算机网络学习笔记
    • 网络安全相关
    • 域名和子网掩码
    • CORS 跨域资源共享
    • DNS、HTTP 与 HTTPS
    • Server-Sent Events (SSE)
    • WebSocket 长连接
  • 计算机基础

    • 操作系统 IO 相关知识
    • 操作系统学习笔记
    • 程序的机器级表示
    • 音频文件基础
    • 正则表达式相关概念
    • ffmpeg 的安装以及实现音频切分功能
    • Hex 和 Base64 编码
    • XML 的使用
  • 数据结构与算法

    • 动态规划算法学习笔记
    • 基于比较的排序算法的最坏情况下的最优下界为什么是O(nlogn)
    • 集合与数据结构学习笔记
    • 面试常见算法总结
    • 算法导论第二部分排序学习笔记
    • 算法导论第一部分学习笔记
  • Java

    • 对象之间的映射与转换
    • 反射学习笔记
    • 泛型相关概念
    • 关于 boolean 类型的坑
    • 如何使用 lambda 表达式实现排序
    • CompletableFuture 相关用法
    • CompletableFuture 源码浅要阅读
    • FutureTask 源码阅读
    • Guava 常用 API
    • Guava 源码阅读:Multimap 相关
    • Jackson 的各种使用
    • Java 的 Excel 相关操作
    • java 的常见性能问题分析以及出现场景
    • java 基础知识
    • JAVA 枚举的基础和原理
    • Java 图片文件上传下载处理
    • Java 序列化
    • Java 异常
    • Java 语法糖
    • Java 中关于字符串处理的常用方法
    • Java 中强、软、弱、虚引用
    • JAVA 注解小结
    • Java Http 访问框架
    • Java Stream 的使用
    • Java8 新特性
    • netty 学习笔记
    • Scanner 的各种用法
    • Servlet 学习笔记
    • String、StringBuffer、StringBuilder 学习笔记
  • JVM

    • 虚拟机执行子系统
    • JVM 自动内存管理
    • Linux 中 JVM 常用工具以及常见问题解决思路
      • JVM 调优
      • JVM 分析命令
        • jstack 分析栈情况
        • jstat 查看堆、gc 情况
        • jmap
        • DUMP 日志内容
      • 调优思路
      • 常见问题
        • 内存泄漏
        • CPU 高
        • 系统平均负载高(load average)
  • Linux

    • crontab 表达式
    • Linux 常见命令
    • Linux 文件系统
  • 中间件

    • 关于定时任务原理
    • 详解 kafka
    • ES 搜索引擎
    • flink 提交流程
    • Grape-RAG
    • Hadoop 基础原理
  • 多线程

    • 多线程基础学习笔记
    • 简单了解并发集合
    • 如何手写单例
    • 深入理解 java 多线程安全
    • 生产者消费者问题
    • 线程池作用、用法以及原理
    • AQS 组件
    • ThreadLocal 原理以及使用
  • 非关系型数据库

    • Redis 集群
    • Redis 数据结构、对象与数据库
    • Redis 学习笔记
  • 关系型数据库

    • B+ 树的插入、删除和数据页分裂机制
    • MySQL 的 binglog、redolog、undolog
    • MySQL 的记录存储结构、存储引擎与 Buffer Pool
    • MySQL 基本的特性
    • MySQL 开发规范
    • MySQL 事务与锁与 MVCC
    • MySQL 数据类型、字符集相关内容
    • MySQL 索引与索引优化
    • PostgreSQL 更新数据时 HOT优化
    • PostgreSQL 相关用法
  • Python

    • Python 基础语法
    • Python 学习
  • Spring 项目

    • Lombok 的常用注解
    • maven 小结
    • MyBatis 框架的使用
    • MyBatis 重要知识点总结
    • MybatisPlus 的使用
    • Spring 框架基础使用
    • Spring 事务相关
    • Spring IOC 的原理及源码
    • Spring AOP 的使用和原理
    • SpringBoot 的原理
    • SpringBoot 基础使用
    • SpringWeb 重要知识点
  • 分布式

    • 初步了解 docker
    • 从 ACID 到 BASE 事务处理的实现
    • 访问远程服务
    • 分布式 id
    • 分布式缓存相关问题
    • 分布式集群理论和分布式事务协议
    • 分布式架构的观测
    • 分布式一致性算法
    • 负载均衡 Load Balancing
    • 关于分布式系统 RPC 中高可用功能的实现
    • 集群间数据同步的目的
    • 三高问题下的系统优化
    • 数据库分库分表
    • 详解 Spring Cloud
    • Dubbo 基础概念
    • Gossip 协议
    • nginx 学习笔记
    • Protobuf 通信协议
    • Zookeeper 基础学习
  • 架构设计

    • 参数校验与异常处理
    • 抽象方法与设计模式
    • 代码整洁之道
    • 权限系统设计
    • 用低内存处理大量数据
    • 设计模式——策略模式
    • 设计模式——过滤器模式在 Spring 中的实践
    • 状态模式
    • 统一结果返回
    • 为什么要打日志?怎么打日志?打什么日志?
    • 运维监控常见指标含义
    • 资深研发进阶
    • DDD 架构学习笔记
    • Java 常用的规则引擎
    • MVC 架构学习笔记
  • AI

    • 如何编写 Prompt
    • Agent 工程架构
    • LLM 相关内容
    • NLP 相关知识
    • vibe coding 最佳实践
    • windows 下 ollama 迁移到 D 盘
  • 开发工具

    • 如何画时序图、流程图、状态流转图
    • excel 关于 =vlookup 的用法
    • git 的学习以及使用
    • IDEA 插件推荐
    • IDEA 常用快捷键以及调试
    • Shell 脚本
    • swagger 的使用
  • 前端

    • 简单了解前端页面开发
    • 伪静态是什么
    • GitHub Pages 部署教程
    • Vercel 部署教程
    • vue-admin-template 简单使用
    • VuePress 博客搭建指南
  • 项目

    • 面试刷题网——技术方案
    • 影视资源聚合站——技术方案
  • 问题记录

    • 定时任务单线程消费 redis 中数据导致消费能力不足
    • 提供可传递的易受攻击的依赖项
    • Liteflow 在 SpringBoot 启动时无法注入组件问题 couldn‘t find chain with the id[THEN(NodeComponent)]
  • 金融

    • 股票分析——关于电力
    • 股票技术面——量价关系
    • 股票技术面——盘口
    • 股票技术面——基础
    • 基础的金融知识
    • 基金与股票
    • 韭菜的自我总结
    • 聊聊价值投资
  • 其他

    • 程序员职场工作需要注意什么
    • 创业全链路SOP:从灵光一现到系统化增长的实战指南
    • 观罗翔讲刑法随笔
    • 价格和价值
    • 立直麻将牌效益理论
    • 梅花易数学习笔记
    • 压力管理
2023-08-17
JVM
目录

Linux 中 JVM 常用工具以及常见问题解决思路

# JVM 调优

正常来说我们一般不会接触到 JVM 调优,一般也不会出现 OOM 问题(就算出现了也大概率不是我们业务代码的问题,而是框架或者工具的问题),不过我们需要看 gc 日志来判断虚拟机的性能怎么样,以优化业务代码

gc 日志主要包含的信息为收集类型(是老年代还是新生代等)、从开始的多少 K 收集到了多少 K、收集的耗时是多少等数据,我们还可以观察 GC 日志的频率看看是否出现了短时间内出现了大量的收集行为。尽量让日志中不要出现 Full GC,因为会 STW,非常影响性能

# JVM 分析命令

除了 gc 日志,一般来说,我们分析会用到以下的命令:

# jstack 分析栈情况

jstack:主要用来查看某个 Java 进程内的线程栈信息,根据堆栈信息我们可以定位到具体代码,具体用法需要先找到线程

第一步先找出 Java 进程 ID,我部署在服务器上的 Java 应用名称为 mrf-center。这里也可以用 top 命令找

root@ubuntu:/## ps -ef | grep mrf-center

root     21711     1  1 14:47 pts/3    00:02:10 java -jar mrf-center.jar
1
2
3

得出进程 ID 为21711,通过进程可以找到进程中的线程,以及线程执行时间、消耗 CPU 和内存资源情况,找出异常数据,通过线程 ID 配合 jstack,就可以输出进程21711的堆栈信息了。里面会包含在哪个类执行哪些方法

root@ubuntu:/## jstack 21711 | grep 54ee

"PollIntervalRetrySchedulerThread" prio=10 tid=0x00007f950043e000 nid=0x54ee in Object.wait() [0x00007f94c6eda000]
1
2
3

同时这个指令也可以看到有哪些线程在等待锁

jstack -l <pid> > thread_dump.txt
1

执行 jstack 需要切换自己的用户到之前执行 java 命令的用户,不然会报错

jstack 可以生成栈转储问题

# jstat 查看堆、gc 情况

jstat 是 JVM 统计监测工具,比如下面输出的是 GC 信息,采样时间间隔为 250ms,需要采样4次。通过这个命令可以看到大概发生了多少次 GC,以及平均 GC 耗时

jstat 命令命令格式:

jstat [Options] vmid [interval] [count]

参数说明:

  • Options,选项,我们一般使用 -gcutil 查看 gc 情况
  • vmid,VM 的进程号,即当前运行的 java 进程号
  • interval,间隔时间,单位为秒或者毫秒
  • count,打印次数,如果缺省则打印无数次
root@ubuntu:/## jstat -gc 21711 250 4
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       PC     PU    YGC     YGCT    FGC    FGCT     GCT   
192.0  192.0   64.0   0.0    6144.0   1854.9   32000.0     4111.6   55296.0 25472.7    702    0.431   3      0.218    0.649
192.0  192.0   64.0   0.0    6144.0   1972.2   32000.0     4111.6   55296.0 25472.7    702    0.431   3      0.218    0.649
192.0  192.0   64.0   0.0    6144.0   1972.2   32000.0     4111.6   55296.0 25472.7    702    0.431   3      0.218    0.649
192.0  192.0   64.0   0.0    6144.0   2109.7   32000.0     4111.6   55296.0 25472.7    702    0.431   3      0.218    0.649
1
2
3
4
5
6

其对应的指标含义如下:

  • S0C 年轻代中第一个 survivor(幸存区)的容量(字节)
  • S1C 年轻代中第二个 survivor(幸存区)的容量(字节)
  • S0U 年轻代中第一个 survivor(幸存区)目前已使用空间(字节)
  • S1U 年轻代中第二个 survivor(幸存区)目前已使用空间(字节)
  • EC 年轻代中 Eden(伊甸园)的容量(字节)
  • EU 年轻代中 Eden(伊甸园)目前已使用空间(字节)
  • OC Old 代的容量(字节)
  • OU Old 代目前已使用空间(字节)
  • MC 方法区大小
  • MU 方法区目前已使用空间(字节)
  • CCSC 压缩类空间大小
  • CCSU 压缩类空间已使用大小
  • YGC 从应用程序启动到采样时年轻代中 gc 次数
  • YGCT 从应用程序启动到采样时年轻代中 gc 所用时间
  • FGC 从应用程序启动到采样时 old 代 gc 次数
  • FGCT 从应用程序启动到采样时 old 代 gc 所用时间
  • GCT 从应用程序启动到采样时 gc 用的总时间

除此之外,我们还可以根据 top 或者 ps 命令,来查看是否有异常的线程占用了过多的 CPU 或者内存资源。使用 jmap 查看详细堆信息,或者打印 dump 日志

# jmap

jdk 安装后会自带一些小工具,jmap 命令 Java Memory Map 是其中之一。主要用于打印指定 Java 进程(或核心文件、远程调试服务器)的共享对象内存映射或堆内存细节

jmap 命令可以获得运行中的 jvm 的堆的快照,从而可以离线分析堆,以检查内存泄漏,检查一些严重影响性能的大对象的创建,检查系统中什么对象最多,各种对象所占内存的大小等等。可以使用 jmap 生成 Heap Dump。其用法如下

1,将数据导出:

jmap -dump:format=b,file=heap.bin <pid>
1

这个时候会在当前目录以生成一个 heap.bin 这个二进制文件

2,通过命令查看大对象

也是使用 jmap 的命令,只不过参数使用 -histo

jmap -histo <pid>|less
1

可得到如下包含对象序号、某个对象示例数、当前对象所占内存的大小、当前对象的全限定名,如下图 在这里插入图片描述 3,还可以查看对象数最多的对象,并按降序排序输出:

jmap -histo <pid>|sort -k 2 -g -r|less
1

4,查看占用内存最多的最象,并按降序排序输出:

jmap -histo <pid>|grep alibaba|sort -k 3 -g -r|less
1

# DUMP 日志内容

Dump 文件可以通过 jmap、jstack 命令生成,一般使用 MAT 工具分析

1,堆转储(Heap Dump),包含 Java 堆内存的完整快照:

  • 所有对象信息:类实例、数组等
  • 对象字段值:包括原始类型和引用
  • 类信息:类名、修饰符、超类、接口等
  • GC Roots:所有 GC 根对象引用链
  • 对象大小:每个对象占用的内存大小

2,线程转储(Thread Dump),包含 JVM 中所有线程的状态信息:

  • 线程列表:所有活动线程
  • 线程状态:RUNNABLE、BLOCKED、WAITING、TIMED_WAITING 等
  • 调用栈:每个线程的完整堆栈跟踪
  • 锁信息:线程持有的锁和等待的锁
  • 线程属性:名称、优先级、守护状态、线程组等
  • 死锁检测:如果存在死锁会标识出来

3,核心转储(Core Dump),包含整个 JVM 进程的内存映像:

  • 完整的进程内存:包括堆、栈、原生内存等
  • 寄存器状态:CPU 寄存器内容
  • 原生栈:本地方法调用的栈信息
  • 共享库信息:加载的共享库状态

我在线上就遇到过,因为日志打的太多,导致内存缓冲区(它是内存空间的一部分。也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,Java 日志框架在将日志写入最终目的地之前,会使用缓冲区来提高性能)空间不足,观察 dump 日志后,发现堆转储中无用的 string 对象过多,观察日志发现,某个经常调用的方法中打印了一个巨大对象,最后将该日志删除解决问题,之前还发现过日志打太多导致的 OOM 问题

# 调优思路

由于调优会修改线上配置,影响较大,我们一般都是先找出哪里的代码写的有问题,实在没办法了,才会进行调优操作

一般修改的参数只有 xms 和 xmx,用于改动堆大小,不得不考虑进行 JVM 调优的是那些情况呢?

  • Heap 内存(老年代)持续上涨达到设置的最大内存值;
  • Full GC 次数频繁;
  • GC 停顿时间过长(超过1秒);
  • 应用出现 OutOfMemory 等内存异常;
  • 应用中有使用本地缓存且占用大量内存空间;
  • 系统吞吐量与响应性能不高或下降。

# 常见问题

# 内存泄漏

没有实际场景的 JVM 调优都是空谈,我们来看看如何定位内存泄漏问题

如果 gc 日志频繁的发生 fullGC,并且回收的内存逐渐变小,内存不足的话,我们就需要判断是否有内存泄漏问题了

我们可以先通过 jmap 查看堆内存

## jmap -heap pid
jmap -heap 27403

Attaching to process ID 27403, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.25-b02

using thread-local object allocation.
Mark Sweep Compact GC

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 1006632960 (960.0MB)
   NewSize                  = 20971520 (20.0MB)
   MaxNewSize               = 335544320 (320.0MB)
   OldSize                  = 41943040 (40.0MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
New Generation (Eden + 1 Survivor Space):
   capacity = 216989696 (206.9375MB)
   used     = 116865984 (111.45208740234375MB)
   free     = 100123712 (95.48541259765625MB)
   53.857849545077016% used
Eden Space:
   capacity = 192937984 (184.0MB)
   used     = 116865984 (111.45208740234375MB)
   free     = 76072000 (72.54791259765625MB)
   60.57178663170856% used
From Space:
   capacity = 24051712 (22.9375MB)
   used     = 0 (0.0MB)
   free     = 24051712 (22.9375MB)
   0.0% used
To Space:
   capacity = 24051712 (22.9375MB)
   used     = 0 (0.0MB)
   free     = 24051712 (22.9375MB)
   0.0% used
tenured generation:
   capacity = 481370112 (459.0703125MB)
   used     = 288820968 (275.4411392211914MB)
   free     = 192549144 (183.6291732788086MB)
   59.99977165179711% used

9569 interned Strings occupying 839416 bytes.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

如果发现老年代的对象越来越多,并且无法被回收,我们就基本可以定位到是内存泄漏的问题了,接下来使用 jmap -dump 导出 dump 文件,可以查看是哪个实例对象过多

一般来说,内存泄漏的情况包含:

  • 使用 ThreadLocal 不当或者
  • 将数据放进 map 时没有重写 equal 和 hashcode 方法的话,会出现内存泄漏问题
  • 集合类中有对对象的引用,使用完后未清空
  • 代码中存在死循环或循环产生过多重复的对象实体

# CPU 高

1,步骤:通过 top 命令查找进程-》查找线程(top 可以找出 CPU 占用最高的线程 ID)-》根据线程 ID 可以通过 jstack 命令定位问题代码-》找出问题代码

a、查看 cpu 高的 java 进程

top
1

b、生成进程下所有线程的栈日志。

jstack 1721 > 1712.txt
1

c、查看进程下哪些线程占用了高的 cpu

top -p 1712 -H

d、将十进制 pid 转换为十六进制的 pid

printf  "%x" 8247

2037
1
2
3

e、在 1712.txt 文件中定位问题 在这里插入图片描述

执行 jmap,jstack 等命令时可能会出现 Unable to open socket file: target process not responding or HotSpot VM not loaded 问题

其实大部分情况是用户错误,切换到进程所在用户执行命令即可

su yarn
1

# 系统平均负载高(load average)

平均负载是指单位时间内,系统处于可运行状态和不可中断状态的平均进程数,也就是平均活跃进程数,它和 CPU 使用率并没有直接关系。一般来说单核 CPU 的 load 不应该大于1,同理,多核的 load 不应该大于核数

我们常见的负载高一般有这几种情况引起,一个是 cpu 密集型,使用大量 cpu 会导致平均负载升高。另外一个就是 io 密集型等待 I/O 会导致平均负载升高,但是 CPU 使用率不一定很高

还有就是大量等待 CPU 的进程调度也会导致平均负载升高,此时的 CPU 使用率也会比较高

#JVM
最后更新: 3/1/2026, 3:55:02 PM
JVM 自动内存管理
crontab 表达式

← JVM 自动内存管理 crontab 表达式→

最近更新
01
vibe coding 最佳实践
02-24
02
立直麻将牌效益理论
02-23
03
伪静态是什么
02-08
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式