Java内存溢出的排查工具和方法

news2025/1/15 20:43:11

JVM内存溢出事故回顾

JVM内存溢出的排查方法个工具介绍

事故回顾
• 9:58收到报警,资讯延时1小时。
• 10:10排查出接口全部超时,超时时间2s。
• 去运维那边执行jstat发现元空间沾满了,疯狂fgc。
• 执行jmap -dump 并下载。
• 使用MAT分析,发现有大量的mongo类(动态生成的,名字前缀一样)

• 排查代码发现mongoTemplate没有使用单例导致。
• 修改代码并压测,使用VisualVM程序各个指标(发现镜像使用不正确导致
线程数不对)

Java 内存区域详解

在这里插入图片描述

01

Java 内存区域详解
对于 Java 程序员来说,在虚拟机自动内存管理机制下,不再需要像 C/C++ 程序开发
程序员这样为每一个 new 操作去写对应的delete/free 操作,不容易出现内存泄漏和
内存溢出问题。正是因为 Java 程序员把内存控制权利交给 Java 虚拟机,一旦出现内
存泄漏和溢出方面的问题,如果不了解虚拟机是怎样使用内存的,那么排查错误将会是
一个非常艰巨的任务。运行时数据区域Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分成若干个不同的数据区域。

 线程私有的:

○ 程序计数器
○ 虚拟机栈
○ 本地方法栈

 线程共享的:

○ 堆
○ 方法区
○ 直接内存 ( 非运行时数据区的一部分)
程序计数器
程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等功能都需要依赖这个计数器来完成。
另外,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。
从上面的介绍中我们知道程序计数器主要有两个作用:
• 字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行选择、循环、异常处理。
• 在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。
注意:程序计数器是唯一一个不会出现 OutOfMemoryError 的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而死亡。

Java 虚拟机栈

与程序计数器一样,Java 虚拟机栈也是线程私有的,它的生命周期和线程相同,描述的是 Java 方法执行的内存模型,每次方法调用的数据都是通过栈传递的。
Java 内存可以粗糙的区分为堆内存(Heap)和栈内存 (Stack),其中栈就是现在说的虚拟机栈,或者说是虚拟机栈中局部变量表部分。 (实际上,Java 虚拟机栈是由一个个栈帧组成,而每个栈帧中都拥有:局部变量表、操作数栈、动态链接、方法出口信息。)
局部变量表主要存放了编译期可知的各种数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)。
Java 虚拟机栈会出现两种错误:StackOverFlowError 和 OutOfMemoryError。
• StackOverFlowError : 若 Java 虚拟机栈的内存大小不允许动态扩展,那么当线程请求栈的深度超过当前 Java 虚拟机栈的最大深度的时候,就抛出 StackOverFlowError 错误。
• OutOfMemoryError : 若 Java 虚拟机堆中没有空闲内存,并且垃圾回收器也无法提供更多内存的话。就会抛出 OutOfMemoryError 错误。
Java 虚拟机栈也是线程私有的,每个线程都有各自的 Java 虚拟机栈,而且随着线程的创建而创建,随着线程的死亡而死亡。

本地方法栈

和虚拟机栈所发挥的作用非常相似,区别是: 虚拟机栈为虚拟机执行 Java方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。 在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一。
本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地
方法的局部变量表、操作数栈、动态链接、出口信息。
方法执行完毕后相应的栈帧也会出栈并释放内存空间,也会出现 StackOverFlowError 和 OutOfMemoryError 两种错误。

Java 虚拟机所管理的内存中最大的一块,Java 堆是所有线程共享的一块内存区域,
在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实
例以及数组都在这里分配内存。
Java世界中“几乎”所有的对象都在堆中分配,但是,随着JIT编译期的发展与逃逸
分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化,所有的
对象都分配到堆上也渐渐变得不那么“绝对”了。从jdk 1.7开始已经默认开启逃逸分
析,如果某些方法中的对象引用没有被返回或者未被外面使用(也就是未逃逸出去)
,那么对象可以直接在栈上分配内存。Java 堆是垃圾收集器管理的主要区域,因此也被称作GC 堆(Garbage Collected Heap).从垃圾回收的角度,由于现在收集器基本都采用分代垃圾收集算法,所以 Java 堆还可以细分为:新生代和老年代:再细致一点有:Eden 空间、From Survivor、To Survivor 空间等。进一步划分的目的是更好地回收内存,或者更快地分配内存。

堆这里最容易出现的就是 OutOfMemoryError 错误,并且出现这种错误之后
的表现形式还会有几种,比如:
1. OutOfMemoryError: GC Overhead Limit Exceeded : 当JVM花太多时间执行垃圾回收并且只能回收很少的堆空间时,就会发生此错误。

  1. java.lang.OutOfMemoryError: Java heap space : 假如在创建新的对象时
    , 堆内存中的空间不足以存放新创建的对象, 就会引发java.lang.OutOfM
    emoryError: Java heap space 错误。(和本机物理内存无关,和你配置
    的内存大小有关!)


方法区

• 方法区与 Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚
拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
虽然 Java 虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一
个别名叫做 Non-Heap(非堆),目的应该是与 Java 堆区分开来。
• 方法区也被称为永久代。
为什么要将永久代 (PermGen) 替换为元空
间 (MetaSpace) 呢?

  1. 整个永久代有一个 JVM 本身设置固定大小上限,无法进行调整,而元空间使用的是直接内存,受本机可用内存的限制,虽然元空间仍旧可能溢出,但是比原来出现的几率会
    更小。
    当你元空间溢出时会得到如下错误: java.lang.OutOfMemoryError: MetaSpace
    你可以使用 -XX:MaxMetaspaceSize 标志设置最大元空间大小,默认值为 unlimited,这意味着它只受系统内存的限制。-XX:MetaspaceSize 调整标志定义元空间的初始大小如果未指定此标志,则 Metaspace 将根据运行时的应用程序需求动态地重新调整大小。
  2. 元空间里面存放的是类的元数据,这样加载多少类的元数据就不由 MaxPermSize 控制了, 而由系统的实际可用空间来控制,这样能加载的类就更多了。
  3. 在 JDK8,合并 HotSpot 和 JRockit 的代码时, JRockit 从来没有一个叫永久代的东西, 合并之后就没有必要额外的设置这么一个永久代的地方了
    运行时常量池
    • 运行时常量池是方法区的一部分。Class 文件中除了有类的版本、字段、
    方法、接口等描述信息外,还有常量池表(用于存放编译期生成的各种字
    面量和符号引用)
    • 既然运行时常量池是方法区的一部分,自然受到方法区内存的限制,当常
    量池无法再申请到内存时会抛出 OutOfMemoryError 错误。
    直接内存
    • 直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义
    的内存区域,但是这部分内存也被频繁地使用。而且也可能导致 OutOfMe
    moryError 错误出现。
    • JDK1.4 中新加入的 NIO(New Input/Output) 类,引入了一种基于通道
    (Channel ) 与缓存区(Buffer ) 的 I/O 方式,它可以直接使用 Native
    函数库直接分配堆外内存,然后通过一个存储在 Java 堆中的 DirectByte
    Buffer 对象作为这块内存的引用进行操作。这样就能在一些场景中显著提
    高性能,因为避免了在 Java 堆和 Native 堆之间来回复制数据。
    • 本机直接内存的分配不会受到 Java 堆的限制,但是,既然是内存就会受
    到本机总内存大小以及处理器寻址空间的限制。

元空间(Metaspace)

02

Metaspace 的组成
Metaspace 由两大部分组成: Klass Metaspace 和NoKlass Metaspace 。
 Klass Metaspace: Klass Metaspace 就是用来存klass 的, klass 是我们熟知的class 文件在jvm 里的运行时数据结构,不过有点要提的是我们看到的类似A.class 其实是存在heap里的,是java.lang.Class 的一个对象实例。这块内存是紧接着Heap 的,和我们之前的perm 一样,这块内存大小可通过-XX:CompressedClassSpaceSize 参数来控制,这个参数前面提到了默认是1G ,但是这块内存也可以没有,假如没有开启压缩指针就不会有这块内存,这种情况下klass 都会存在NoKlass Metaspace 里,另外如果我们把-Xmx 设置大于32G 的话,其实也是没有这块内存的,因为会这么大内存会关闭压缩指针开关。还有就是这
块内存最多只会存在一块。
 NoKlass Metaspace: NoKlass Metaspace 专门来存klass 相关的其他的内容,比如
method , constantPool 等,这块内存是由多块内存组合起来的,所以可以认为是不连续
的内存块组成的。这块内存是必须的,虽然叫做NoKlass Metaspace ,但是也其实可以存
klass 的内容,上面已经提到了对应场景。
Metaspace 内存管理
在metaspace 中,类和其元数据的生
命周期与其对应的类加载器相同,只
要类的类加载器是存活的,在
Metaspace 中的类元数据也是存活
的,不能被回收。
每个加载器有单独的存储空间。
省掉了GC 扫描及压缩的时间。
当GC 发现某个类加载器不再存活了,
会把对应的空间整个回收。
排查工具

03

# 排查工具(Jstat )

监视元空间大小的最简单方法是使用JDK中提供的jstat工具。当与选项-gc一起使
用时,它提供以下信息:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jQOc8iGj-1691029218560)(/uploads/course_share/images/m_415b514fe979746a41e144d7f2bc3547_r.png)]

• S0C:第一个Survivor 区的大小
• S1C:第二个Survivor 区的大小
• S0U:第一个Survivor 区的使用大小
• S1U:第二个Survivor 区的使用大小
• EC:Eden区的大小
• EU:Eden区的使用大小
• OC:老年代大小
• OU:老年代使用大小
• MC:方法区大小
• MU:方法区使用大小
• CCSC:Klass Metaspace 空间大小
• CCSU:Klass Metaspace 使用大小
• YGC:新生代垃圾回收次数
• YGCT:新生代垃圾回收消耗时间
• FGC:老年代垃圾回收次数
• FGCT:老年代垃圾回收消耗时间
• GCT:垃圾回收消耗总时间

VisualVM

VisualVM,能够监控线程,内存情况,查看方法
的CPU时间和内存中的对 象,已被GC的对象,
反向查看分配的堆栈(如100个String对象分别由
哪几个对象分配出来的).
从界面上看还是比较简洁的,左边是树形结构,自
动显示当前本机所运行的Java程序,还可以添加
远程的Java VM,其中括号里面的PID指的是进
程ID。OverView界面显示VM启动参数以及该V
M对应的一些属性。Monitor界面则是监控Java
堆大小,Permgen大小,Classes和线程数量。j
dk不同版本中界面会不太一致,如有的含cpu监控
,有的则不含(jdk1.6.0_10 未包含)。
MAT
MAT 是Memory Analyzer tool 的
缩写,是一种快速,功能丰富的Java
堆分析工具,能帮助你查找内存泄漏
和减少内存消耗。很多情况下,我们
需要处理测试提供的hprof 文件,分
析内存相关问题,那么MAT 也绝对
是不二之选。 Eclipse 可以下载插件
结合使用,也可以作为一个独立分析
工具使用,下载地址:
https://www.eclipse.org/mat/do
wnloads.php
延伸点
 上面我们使用工具jump 了,那怎么去服务器上jump 呢?
○ 找运维 jmap -dump:format=b,file=<dumpfile.hprof>
 jump 出来的文件怎么下载呢?
○ 云平台的文件下载入口,找容器配置的第一个运维审核即可。
排查方向

04 重点排查

以下几点
 检查代码中是否有死循环或递归调用。
 检查是否有大循环重复产生新对象实体。
 检查List 、MAP 等集合对象是否有使用完后
,未清除的问题。List 、MAP 等集合对象会
始终存有对对象的引用,使得这些对象不能被
GC 回收。
 检测是否频繁动态生成类。
 使用内存查看工具动态查看内存使用情况。
THANKS

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

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

相关文章

VLAN原理+配置

目录 一&#xff0c; 以太网二层交换机 二&#xff0c;三层架构&#xff1a; 三&#xff0c;VLAN配置思路 1.创建vlan 2.接口划入vlan 3.trunk干道 4.vlan间路由器 5.DHCP池塘配置 四&#xff0c;华为VLAN部分的接口模式讲解&#xff1a; 五&#xff0c;华为VLAN部分的…

【雕爷学编程】MicroPython动手做(30)——物联网之Blynk 2

知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通信&#xff0c;可作为物联网节点&#xff0c;实现物联网应用。同时掌控板上集成了OLED…

阿里云出品—高分计算机好书推荐榜

1、云原生架构白皮书 云原生是一种构建和运行应用程序的方法&#xff0c;它能实现构建应用简便快捷&#xff0c;部署应用轻松自如&#xff0c;越来越多公司和个人选择使用云原生技术。《云原生架构白皮书》作为业界首本全方位构建云原生架构规划与实践全景图的白皮书&#xff…

【牛客】统计字符

⭐️ 题目描述 &#x1f31f; OJ链接&#xff1a;HJ40 统计字符 ps&#xff1a; 判断字符可以直接使用头文件自带的函数。 函数作用iscntrl判断是否为控制字符isspace判断是否为空白字符&#xff08;空格、换页’\f’、换行’\n’、回车’\r’、制表符’\t&#xff09;isdigi…

「应用实时监控 ARMS 」斩获「根因分析技术」先进级认证

阿里云云原生可观测 ARMS 率先斩获「根因分析技术」先进级认证 7 月 25 日&#xff0c;由中国信通院发起的“2023 可信云-系统稳定性”首批评估结果在可信云大会现场公布&#xff0c;应用实时监控服务 ARMS 斩获《可观测性标准体系要求 - 根因分析技术分级能力要求》“先进级”…

Pytorch深度学习之余弦退火学习率设置

1. 什么是余弦退火学习率&#xff1f; 余弦退火学习速率调度是改进深度神经网络学习过程的常用方法。当深度神经网络在大型数据集上训练时&#xff0c;它尤其有用&#xff0c;因为在大型数据集中&#xff0c;学习过程可能会陷入局部极小值。在训练过程中&#xff0c;学习率以不…

OpenMMLab【超级视客营】——把类别信息加入可视化结果中(MMSegmentation的第二个PR)

文章目录 1. 任务说明1.0 新手指引1.1 任务目标1.2 提交格式 2. 实施2.1 可视化的形式2.2 拉分支和提交PR2.2.1 拉分支2.2.2 提交PR 2.3 MMSegmentation中关于可视化的内容2.3.1 文档说明2.3.2 相关PR&#xff08;确定要修改的文件&#xff09;2.3.3 提交时的代码测试 2.4 发现…

java实现5种不同的验证码图片,包括中文、算式等,并返回前端

导入以下依赖 <!--图片验证码--><dependency><groupId>com.github.whvcse</groupId><artifactId>easy-captcha</artifactId><version>1.6.2</version></dependency> 编写controller package com.anXin.user.controlle…

Tessy 4.3.18

Tessy 4.3.18 windows 2692407267qq.com&#xff0c;更多内容请见http://user.qzone.qq.com/2692407267/

【无标题】uniapp引入萤石云 真机无法运行 踩坑集合

Uniapp 接入萤石云 踩坑 1.先用了 UIKit Javascript 就是在 pc端 那套流程 npm install ezuikit-jsimport EZUIKit from ezuikit-js;这套流程貌似只适用于pc端&#xff0c;我在接入uniapp的时候没看官网 以为都是一套流程&#xff0c;然后就在uniapp中也来了这一套&#xff0…

vue+neo4j(neo4j desktop安装和使用)

vueneo4j&#xff08;neo4j desktop安装和使用&#xff09; 本文目录 vueneo4j&#xff08;neo4j desktop安装和使用&#xff09;官网下载安装基本使用创建项目新增数据库连接数据库 使用cypher构建简单知识图谱创建节点创建关系删除节点及关系查询节点和关系 数据导出为json文…

分布式锁(Redis分布式锁)

Redis分布式锁原理及应用 前言一、基本原理1.1 什么是分布式锁1.2 分布式锁满足的条件1.3 常见的分布式锁 二、Redis分布式锁的实现核心思路2.1 实现分布式锁时需要实现的两个基本方法2.2 核心思路 三、实现分布式锁版本四、Redis分布式锁误删情况说明4.1 逻辑说明4.2 解决方案…

FreeRTOS(4):软件定时器、中断管理

目录 一、延时函数 延时函数分类 vTaskDelay 与 HAL_Delay 的区别 二、软件定时器 什么是定时器&#xff1f; 软件定时器优缺点 软件定时器原理 软件定时器相关配置 单次定时器和周期定时器 1. 创建软件定时器 2. 开启软件定时器 3. 停止软件定时器 4. 复位软件定时…

【剑指 Offer 27】二叉树的镜像

题目&#xff1a; 请完成一个函数&#xff0c;输入一个二叉树&#xff0c;该函数输出它的镜像。 输入&#xff1a;root [4,2,7,1,3,6,9] 输出&#xff1a;[4,7,2,9,6,3,1] 输入输出样例 思考1&#xff1a; 二叉树的镜像&#xff0c;就是交换二叉树的每个节点的左右结点 所…

应用在多媒体手机中的低功率立体声编解码器

多媒体手机一般是指可以录制或播放视频的手机。多媒体的定义是多种媒体的综合&#xff0c;一般是图像、文字、声音等多种结合&#xff0c;所以多媒体手机是可以处理和使用图像文字声音相结合的移动设备。目前流行的多媒体概念&#xff0c;主要是指文字、图形、图像、声音等多种…

【数据结构】图文并茂,通过逻辑图带你轻松拿捏链表,实现各种接口功能(2)

君兮_的个人主页 勤时当勉励 岁月不待人 C/C 游戏开发 Hello,米娜桑们&#xff0c;这里是君兮_&#xff0c;我们接着之前讲过的顺序表来继续介绍初阶数据结构的内容&#xff0c;今天给大家带来的是有关链表的基本知识和各种接口功能的实现的第二部分。 好了&#xff0c;废话不…

Gitignore忽略文件

默认情况下&#xff0c;Git会监视我们项目中的所有内容&#xff0c;但是有些内容比如mode_modules中的内容&#xff0c;我们不希望他被Git所管理。 我们可以在我们项目目录中添加一个 .gitignore 文件来设置那些需要git忽略的文件。

Burpxss自动化测试工具validator配置和使用教程

一、配置教程 下载Phantomjs&#xff1a; http://phantomjs.org/download.html 下载xss.js https://github.com/nVisium/xssValidator 将xss.js和phantomjs.exe放在一起 利用phantomjs运行xss.js C:\xss>phantomjs.exe xss.js Bapp store里搜索xss validator,然后安装它 安…

基于jeecg-boot的flowable流程提供一种动态设置发起人部门负责人的方式

更多功能看演示系统 gitee源代码地址 后端代码&#xff1a; https://gitee.com/nbacheng/nbcio-boot 前端代码&#xff1a;https://gitee.com/nbacheng/nbcio-vue.git 在线演示&#xff08;包括H5&#xff09; &#xff1a; http://122.227.135.243:9888 这里给大家提供一种…

node.js系列-常见问题处理方案(持续更新)

问题1&#xff1a;nodejs 如何使用 atob、btoa 解决方案&#xff08;base64与uint8array转换&#xff09;&#xff0c;btoa和atob在nodejs中应该怎么写&#xff1f; 浏览器中我们可以这样使用&#xff1a; btoa(123456) MTIzNDU2 atob(MTIzNDU2) 123456node.js中实现方案 con…