【JVM内存】系统性排查JVM内存问题的思路

news2024/9/22 13:41:01

【JVM内存】系统性排查JVM内存问题的思路

背景

前言

  • 遇到过几次JVM堆外内存泄露的问题,每次问题的排查、修复都耗费了不少时间,问题持续几月、甚至一两年
  • 我们将这些排查的思路梳理成一套系统的方法,希望能给对JVM内存分布内存泄露问题有更清晰的理解。

这篇文章能带给你什么

  • 了解JVM的内存分布.
  • 更合理地去设置JVM参数。
  • 能大大提升排查JVM内存问题的效率。

本文的限定范围

  • JDK版本
  • JDK8*,其他JDK版本可能有所差异。**
  • 重点讲解堆外内存**
  • 堆内的内存问题文章比较多,一般是dump堆内存,然后分析即可。

文章讲解的顺序

  • 讲解JVM内存分布,了解有哪些内存区域、JVM参数等。堆内相关的文章比较多,堆外的比较少,所以重点讲解堆外的。
  • 讲解排查JVM内存问题的思路。

JVM内存分布

JVM内存分布

  • 【重点中的重点】JVM内存分布图
  • 总体分为堆内内存、堆外内存。
  • 在这里插入图片描述

【重点】Heap Space(堆内内存)

  • 重点关注新生代老年代

Young Generation新生代

  • 用于存放新创建的对象,分为一个Eden区两个Survivor区
  • Young GC发生时会回收该块内存。

老年代(Old Generation)

作用
  • 主要用于存放生命周期较长的对象。
何时回收
  • Old GC发生时会回收该块内存,一般触发Old GC时会伴随着一次Young GC
参数
  • -Xmn: 新生代的内存大小
  • -Xms: Heap的初始大小
  • -Xmx: Heap的最大大小
问答

【重点】Non-Heap Space(非堆内存、堆外内存)

什么是堆外内存

  • Non-Heap Space 翻译为非堆内存,也被称为Off-Heap(堆外内存),大家习惯于叫这部分内存为堆外内存。查看了很多国内外文章,对于这块内存,没有很统一的定义

  • 较可信的是分为下面两种定义:

  • 广义上的Non-Heap

  • 除开Heap以外的所有内存,包括MetaSpace、NativeMemory(JNI Memory、Direct Memory等)、Stack、Code Cache等。

  • 下面讲解的Non-Heap是针对于广义的定义

  • 狭义上的Non-Heap

  • 只包含Metaspace、code_cache。

  • 注意:

  • 监控系统里会有Non-Heap的监控,例如SkyWalking、Arthas的Non-Heap指标,都是通过JDK自带的MemoryMXBean方法获取的。

  • 所以一般监控系统采集的Non-Heap只是Heap以外的一部分内存!还需要留意NativeMemory等等内存。

  • 监控数据示例;

  • 在这里插入图片描述

  • 对应的代码:

  • @Override
    public long getNonHeapMemoryMax() {
      return memoryMXBean.getNonHeapMemoryUsage().getMax();
    }
    
    @Override
    
    public long getNonHeapMemoryUsed() {
      return memoryMXBean.getNonHeapMemoryUsage().getUsed();
    }
    
    

【重点】MetaSpace(元数据空间)

  • 用于存储类元数据(如类定义和方法定义)的内存区域。Metaspace 在 JDK 8 中取代了永久代(PermGen)。
相关参数
  • -XX:MetaspaceSize=
  • -XX:MaxMetaspaceSize=
  • -XX:MetaspaceSize 参数设置了元空间的初始大小,在 JDK 8 中,-XX:MetaspaceSize 参数的默认值为 21 MB。。当元空间使用量达到这个值时,JVM 将触发 Full GC(也会附带younggc) 来尝试回收不再需要的类元数据以及相关资源。
  • 如果回收后元空间仍然无法满足需求,那么 JVM 将尝试扩展元空间的大小。
  • 问答:很多同学奇怪,我们有时看到某些应用启动一段时候,堆内存使用量不高,为何会发生一次FULL GC?
  • 这很可能是因为应用的JVM参数里没有设置-XX:MetaspaceSize,或者-XX:MetaspaceSize设置的比较小。
  • -XX:MaxMetaspaceSize 参数设置了元空间的最大大小。元空间会根据需要动态扩展,但不会超过这个设置的最大值。当元空间使用量超过这个值时,JVM 将触发 Full GC(也会附带younggc),尝试回收不再需要的类元数据以及相关资源。如果回收后元空间仍然无法满足需求,那么 JVM 将抛出java.lang.OutOfMemoryError: Metaspace错误。因此,这个参数既与 Full GC 相关,也与 OOM 相关。
问答
  • 如何合理设置-XX:MaxMetaspaceSize参数?
  • 建议JVM启动参数指定-XX:MaxMetaspaceSize,一般大小256M足够,因为默认值无限大,如果出现频繁加载class等情况,容易出现OOM。

OOM异常
  • OOM报错: java.lang.OutOfMemoryError: Metaspace

Native Memory(本地内存)

Direct Memory(直接内存)
JNI Memory(JNI内存)
  • JNI (Java Native Interface) memory是指Java应用程序与本地代码交互时使用的内存。Java Native Interface (JNI) 是 Java 与本地(如 C 或 C++)代码进行交互的桥梁。

  • JNI方法
  • 使用方式:在Java中使用native关键字定义方法,并在C/C++代码中实现相关的本地方法。

  • 示例:

  • private native int inflateBytes(long addr, byte[] b, int off, int len);
    
  • 该native方法内部也会申请内存用以存储数据,这部分内存属于JNI内存的一部分。

  • 参数
  • 无特定的 JVM 参数,但需要在本地代码中管理内存分配和释放。

  • 注意:与-XX:MaxDirectMemorySize=无关。

  • JNI内存分配过程
  • 在这里插入图片描述

Stack(栈内存)

Stack介绍
  • 用于存储线程执行过程中的局部变量、方法调用、操作数栈等。
  • 栈内存由JVM自动管理,每个线程都有一个独立的栈
  • 栈内存与堆内存相互独立,它们之间不共享数据。
  • 分为VM Stack(Java虚拟机栈)、Native Stack(本地方法栈)
分类

特殊内存

MMap

【重点】内存排查工具

堆内内存相关工具

  • 整理了堆内内存相关的工具。
  • 建议从上往下逐一执行命令,从整体到局部,逐步排查出具体的问题。
  • 在这里插入图片描述
堆外内存相关工具
  • 不同的内存区域可以使用不同的命令进行排查,同时也留意合理设置对应内存区域的参数。
  • 在这里插入图片描述

JVM内存使用量过大问题排查思路

整体的排查思路

  • 使用量大原因一般分为

  • 数据量大,自然使用量大

  • JVM内存泄露,导致可以释放的内存未释放

  • JVM内存泄露:

  • 在JVM运行过程中,由于(1)未正确释放不再使用的内存 (2)或者执行内存释放步骤后内存却未回收,导致内存占用持续增长,甚至最终耗尽导致OOM(内存溢出)的现象

  • 发现问题、提前预知问题

  • 依赖于监控告警:falcon、prometheus、troy等,主要是内存、GC相关

  • 发现问题、提前预知问题

  • 先止损,一般处理方式是通过重启,或者手动触发fullgc。

  • 保留现场

  • 如果条件允许一定不要直接操作重启、回滚等动作恢复,优先通过摘掉流量的方式来恢复,例如:通过dubbo控制台将某个provider实例禁止访问。

  • 然后将堆(手工dump、或者指定-XX:+HeapDumpOnOutOfMemoryError)、栈(jstack命令导出)、GC 日志等关键信息保留下来,不然错过了定位根因的时机,后续想要复现、解决的难度将大大增加。

  • 确定是那个进程的问题

  • 当出现内存问题时,需要确认是那个进场的问题。

  • 当发生进程A被操作系统的OOM-killer杀掉时,可能不是A的问题,可能是进程B占用内存过多,导致系统内存不够用,

  • 然后触发OOM-killer计算出oom分数(根据内存、进程运行时间等打分,参考文档),选择杀掉了进程A。

  • 分析日志

  • 分析应用日志是否有outofmemory等关键字;

  • 分析系统日志/var/log/messages或者dmesg观察outofmemory的情况、进程运行的记录;

  • 分析应用GC日志;

  • 查找不同内存区域占比判断可疑的内存

  • 根据命令、监控平台,逐个分析内存区域大户:Heap、MetaSpace、DirectMemory、JNI Memory。

  • 分析可疑内存数据内容

  • 分析内存占用大的区域中的数据,也可以辅助定位对应源码。

  • 分析可疑内存调用栈

  • 对于java而言,推荐使用arthas的trace和stack命令,但是arthas无法对native方法进行拦截,此时可以借助jstack或者arthas拦截可能调用native方法的上层方法。

  • 对于JNI Memory,这块内存是C、C++等native方法相关的,需要用gperftools、gdb等工具进行分析。

  • 复现问题

  • 在没有了解问题原因、内存增长规律的情况下,想要复现问题,有时是很困难的!可能要花费很长时间、且需要些运气。

  • 所以我们尽量保留问题现场,方便找出规律。

  • 内存泄漏按发生方式来分类:

  • 在这里插入图片描述

  • 修复问题

  • JVM内存问题一般是代码问题、JVM参数问题、malloc内存分配库等,针对不同类型的问题进行修复。

案例

  • 案例遇到比较多:
  • 不合理地使用fastjson,导致频繁地在创建、加载class (2)未设置-XX:MaxMetaspaceSize 导致了内存一直增长,直到OOM
  • JNI Memory内存泄漏
  • JVM参数-XX:SoftRefLRUPolicyMSPerMB和metaspace导致的fullgc
  • vim命令编辑文件导致的业务应用的进程被oom-killer杀掉

总结

  • 首先是看这张图,了解JVM内存的分布。

  • 在这里插入图片描述

  • 遇到内存问题,先根据通用的排查思路一遍内存的使用情况。

  • 有很多JDK、Linux内存相关的命令,大家可以去尝试一下,先查大范围的内存占用,再逐步定位到具体的内存区域、代码、参数等。

  • 重启程序、系统能临时解决很多内存问题,但是,建议去深究一下,会学到很多JVM内存管理和Linux内存管理的知识,还是很有趣的。

  • 此外,掌握了JVM内存管理的设计后,发现很多程序的内存是比较浪费的,可以对JVM参数做针对性优化,能减少很多机器资源。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1976966.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

有序矩阵中第K小的元素(LeetCode)

题目 给你一个 n x n 矩阵 matrix ,其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素。 请注意,它是 排序后 的第 k 小元素,而不是第 k 个 不同 的元素。 你必须找到一个内存复杂度优于 的解决方案。 解题 from queue i…

DFS之搜索顺序与剪枝

搜索顺序: 1.https://www.acwing.com/problem/content/1119/ 首先,我们考虑一个贪心: 假如说A的倒数K个字符恰好与B的前K个字符重合,那么我们就连接。 也就是说我们一旦匹配就直接相连而不是继续找更长的重合的一段子串。 因…

秋招突击——算法练习——8/3——马上消费笔试总结——{距离为一的字符串、组合数遍历}

文章目录 引言正文第一题:距离为1的字符串个人实现修正实现 第二题:三角形数个人实现反思实现比较对象使用equalsCollections.sort方法 总结 引言 今天的笔试难度不算大,但是自己的做的很糟糕,发现了很多问题,很多模板…

目标检测,目标跟踪,目标追踪

个人专做目标检测,目标跟踪,目标追踪,deepsort。YOLOv5 yolov8 yolov7 yolov3运行指导、环境配置、数据集配置等(也可解决代码bug),cpu,gpu,可直接运行,本地安装或者远程…

JVM-类加载器和双亲委派机制

什么是类加载器? 类加载器是Jvm的重要组成之一(类加载器、运行时数据区、执行引擎、本地库接口、本地方法库),负责读取java字节码并将其加载到Jvm中的组件 类加载器的分类 Java中的类加载器可以分为以下几种: 1. 启…

Yolov8在RK3588上进行自定义目标检测(一)

1.数据集和训练模型 项目地址:https://github.com/airockchip/ultralytics_yolov8.git 从github(htps:l/github.com/airockchip/ultralytics_yolov8)上获取yolov8模型。 下载项目: git clone https://github.com/airockchip/ultralytics_yolov8.git …

进程的虚拟内存地址(C++程序的内存分区)

严谨的说法: 一个C、C程序实际就是一个进程,那么C的内存分区,实际上就是一个进程的内存分区,这样的话就可以分为两个大模块,从上往下,也就是0地址一直往下,假如是x86的32位Linux系统&#xff0c…

InstantID节点安装遇到的问题与解决办法

原来在一台没有显卡支持的电脑上安装InstantID节点使用没有问题,将安装好的ComfyUI(简称ComfyUI_CPU_OK包)复制到一台有显卡支持的电脑上,竟然发现InstantID节点异常不能使用(按道理应该能正常运行才对)&am…

吴恩达机器学习L1W3L06-逻辑回归的梯度下降

目标 在本实验室,你可以看到 更新逻辑回归的梯度下降。在一个熟悉的数据集上探索梯度下降 import copy, math import numpy as np %matplotlib widget import matplotlib.pyplot as plt from lab_utils_common import dlc, plot_data, plt_tumor_data, sigmoid,…

SQL注入实例(sqli-labs/less-3)

0、初始页面 1、确定闭合字符 确定为字符型注入 ?id1 and 11 ?id1 and 12 确定闭合字符为 ‘) ?id1 ?id1) 2、确定表的列数 确定查询表的列数为3 ?id1) order by 3 -- 3、确定回显位置 确定回显位置为第二列和第三列 ?id-1) union select 1,2,3 -- 4、爆库名 …

MySQL数据库——数据库基础

二、数据库基础 1.主流数据库 SQL Sever:微软的产品,.Net程序员的最爱,中大型项目。Oracle:甲骨文产品,适合大型项目,复杂的业务逻辑,并发一般来说不如MySQL。MySQL:世界上最受欢迎的数据库,属…

《Advanced RAG》-01-朴素RAG存在的问题

摘要 文章阐述了RAG技术如何通过整合外部知识源来提升大型语言模型(LLM)的性能,使其能够产生更精确、上下文感知的回应,并减少幻觉现象。 自2023年以来,RAG已成为基于LLM的系统中最流行的架构,许多产品依赖…

一文彻底搞懂Fine-tuning - 预训练和微调(Pre-training vs Fine-tuning)

最近这一两周看到不少互联网公司都已经开始秋招提前批了。不同以往的是,当前职场环境已不再是那个双向奔赴时代了。求职者在变多,HC 在变少,岗位要求还更高了。 最近,我们又陆续整理了很多大厂的面试题,帮助一些球友解…

推荐算法学习记录2.2——kaggle数据集的动漫电影数据集推荐算法实践——基于内容的推荐算法、协同过滤推荐

1、基于内容的推荐: 这种方法根据项的相关信息(如描述信息、标签等)和用户对项的操作行为(如评论、收藏、点赞等)来构建推荐算法模型。它可以直接利用物品的内容特征进行推荐,适用于内容较为丰富的场景。‌…

VBA学习(22):动态显示日历

这是在ozgrid.com论坛上看到的一个贴子,很有意思,本来使用公式是可以很方便在工作表中实现日历显示的,但提问者因其需要,想使用VBA实现动态显示日历,即根据输入的年份和月份在工作表中显示日历。 下面是实现该效果的VB…

web、nginx

一、web基本概念和常识 ■ Web:为用户提供的一种在互联网上浏览信息的服务,Web服务是动态的、可交互的、跨平台的和图形化的。 ■ Web 服务为用户提供各种互联网服务,这些服务包括信息浏览服务,以及各种交互式服务,包括聊天、购物、学习等等内容。 ■ Web 应用开发也经过了几…

C#中计算矩阵(数学库下载和安装)

1、一步步建立一个C#项目 一步步建立一个C#项目(连续读取S7-1200PLC数据)_s7协议批量读取-CSDN博客文章浏览阅读1.7k次,点赞2次,收藏4次。这篇博客作为C#的基础系列,和大家分享如何一步步建立一个C#项目完成对S7-1200PLC数据的连续读取。首先…

如何解决C#字典的线程安全问题

前言 我们在上位机软件开发过程中经常需要使用字典这个数据结构,并且经常会在多线程环境中使用字典,如果使用常规的Dictionary就会出现各种异常,本文就是详细介绍如何在多线程环境中使用字典来解决线程安全问题。 1、非线程安全举例 Dictio…

文件搜索 36

删除文件 文件搜索 package File;import java.io.File;public class file3 {public static void main(String[] args) {search(new File("D :/"), "qq");}/*** 去目录搜索文件* param dir 目录* param filename 要搜索的文件名称*/public static void sear…

探索Prefect:Python自动化的终极武器

文章目录 探索Prefect:Python自动化的终极武器背景:自动化的呼唤Prefect:自动化的瑞士军刀安装Prefect:一键启动基础用法:Prefect的五招场景应用:Prefect的实战演练常见问题:Prefect的故障排除总…