Linux: 性能分析之内存增长和泄漏

news2024/11/17 13:42:29

文章目录

  • 1. 前言
  • 2. 背景
  • 3. 内存增长和泄漏分析方法
    • 3.1 跟踪 malloc(), free() 等接口
      • 3.1.1 用 perf 采样
      • 3.1.2 用 ebpf 来跟踪
    • 3.2 跟踪 brk() 调用
      • 3.2.1 使用 perf 跟踪 brk()
      • 3.2.2 使用 ebpf 跟踪 brk()
    • 3.3 跟踪 mmap() 调用
      • 3.3.1 使用 perf 跟踪 mmap()
      • 3.3.2 使用 ebpf 跟踪 mmap()
    • 3.4 跟踪 page fault
      • 3.4.1 使用 perf 跟踪 page fault
      • 3.4.2 使用 ebpf 跟踪 page fault
  • 4. 小结
  • 5. 参考链接

1. 前言

限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。

2. 背景

你的应用程序内存使用量正在稳步增长,并且你着急上火,正在争分夺秒地修复它。这可能是由于配置错误而导致的内存增长,也可能是由于软件问题导致的内存泄漏。对于某些应用程序,性能可能会开始下降,由于内存开始紧张,内存压缩规整回收工作将消耗更多的 CPU。如果应用程序占用的内存变得太大,性能可能会由于页面频繁换入换出(swap)而出现断崖式下降,或者应用程序可能会被系统杀死(如 Linux 的 OOM-killer)。你希望在前述任一情况发生之前快速查看一下状况,以便修正问题。但是怎么做呢?
调试内存增长,涉及到对应用程序自身配置的做检查和使用系统相关工具。相对来说,调试内存泄漏要更加困难,但有许多工具可以提供帮助:
(1) mcheck
(2) wrap 内存接口
(3) 模拟 CPU 执行的 ValgrindValgrind 可导致程序运行速度慢上20到30倍,所有有些问题不一定能够重现。
(4) libtcmalloc的堆采样分析器,可能导致程序运行速度降低5倍左右
(5) 使用 coredump 调试的 GDB
以上这些工具,都无法动态的观测到应用程序内存的实时变化情况,不利于我们找到真正导致内存增长或泄漏的根因。本文将介绍4种对内存进行动态跟踪的方法,并将这些跟踪数据可视化为火焰图。本文所有演示基于 Linux 操作系统进行。另外,需要预先部署 FlameGraph 脚本工具。本文生成的一些结果数据可以从 此处 查看。

3. 内存增长和泄漏分析方法

下图说明了本文将介绍的这4种方法,方法涉及图中绿色文本标注的事件:
在这里插入图片描述
这4种方法每种都有自身的缺点,后文将一一加以说明。这些方法需要堆栈追踪记录,很多应用程序使用 GCC 的编译选项 -fomit-frame-pointer 进行编译,这样会导致基于栈帧指针的栈回溯的失败;而像 Java VM 等在运行时进行编译,在没有额外辅助的情形下,追踪工具无法获取程序的符号信息,栈记录将显示为符号地址。这些情形可参考 Stack Traces 和 JIT Symbols 进行处理。

3.1 跟踪 malloc(), free() 等接口

3.1.1 用 perf 采样

# perf record -F 99 -a --call-graph dwarf
# perf script | ./stackcollapse.pl | \
	./flamegraph.pl --color=mem --title="malloc() Flame Graph" --countname="calls" > out.svg

3.1.2 用 ebpf 来跟踪

# /usr/share/bcc/tools/stackcount -p 2990 -U c:malloc > out.stacks
# ./stackcollapse.pl < out.stacks | \
	./flamegraph.pl --color=mem --title="malloc() Flame Graph" --countname="calls" > out.svg

章节 3.1.13.1.2 都最终生成火焰图 out.svg 。直接跟踪 malloc() 引入的开销比较高。直接跟踪 brk() 系统调用会降低开销,但不能够跟踪内存泄漏问题。

3.2 跟踪 brk() 调用

有的应用程序在初始化时,可能通过 brk() 系统调用分配一大块虚拟内存,然后封装自己的内存分配释放接口来响应内存分配释放请求,而不使用 malloc()/free() 接口。
使用 brk() 系统调用分配内存的方式,可能不是那么常见,用 perf 在我的观察了很长时间,也没发现一次 brk() 系统调用:

# perf stat -e syscalls:sys_enter_brk -I 1000 -a
#           time             counts unit events
     1.001318396                  0      syscalls:sys_enter_brk                                      
     2.002291392                  0      syscalls:sys_enter_brk                                      
     3.003251158                  0      syscalls:sys_enter_brk                                      
     4.004399897                  0      syscalls:sys_enter_brk                                      
     5.005382548                  0      syscalls:sys_enter_brk                                      
     6.006749931                  0      syscalls:sys_enter_brk                                      
     7.007859563                  0      syscalls:sys_enter_brk                                      
     8.009513993                  0      syscalls:sys_enter_brk                                      
     9.011043102                  0      syscalls:sys_enter_brk
[...]

3.2.1 使用 perf 跟踪 brk()

# perf record -e syscalls:sys_enter_brk -a -g -- sleep 120
# perf script > out.stacks
# ./stackcollapse-perf.pl < out.stacks | ./flamegraph.pl --color=mem \
    --title="Heap Expansion Flame Graph" --countname="calls" > out.svg

3.2.2 使用 ebpf 跟踪 brk()

# /usr/share/bcc/tools/stackcount SyS_brk > out.stacks
# ./stackcollapse.pl < out.stacks | ./flamegraph.pl --color=mem \
    --title="Heap Expansion Flame Graph" --countname="calls" > out.svg

3.3 跟踪 mmap() 调用

应用程序在初始化时,也可能使用 mmap() 系统调用来分配虚拟内存,用于自己大块数据处理事务。glibc 在分配大块内存时,也可能通过 mmap() 系统调用。如同 brk() 一样,mmap() 的调用频率应该也不高。

3.3.1 使用 perf 跟踪 mmap()

# perf record -e syscalls:sys_enter_mmap -a -g -- sleep 60
# perf script > out.stacks
# ./stackcollapse-perf.pl < out.stacks | ./flamegraph.pl --color=mem \
    --title="mmap() Flame Graph" --countname="calls" > out.svg

3.3.2 使用 ebpf 跟踪 mmap()

# /usr/share/bcc/tools/stackcount SyS_mmap > out.stacks
# ./stackcollapse.pl < out.stacks | ./flamegraph.pl --color=mem \
    --title="mmap() Flame Graph" --countname="calls" > out.svg

3.4 跟踪 page fault

brk()mmap() 影响的是虚拟内存的变更,通常是直到向虚拟地址空间进行写入时,才会通过缺页异常(page fault)分配物理内存。
先用 perf 跟踪一下缺页异常的情况:

# perf stat -e page-faults -I 1000 -a
#           time             counts unit events
     1.000995850                  0      page-faults                                                 
     2.002827305                  2      page-faults                                                 
     3.003792700                  0      page-faults                                                 
     4.005305961                  0      page-faults                                                 
     5.006339296                  0      page-faults                                                 
     6.007330095                  0      page-faults                                                 
     7.008390464                  0      page-faults                                                 
     8.009137206                  0      page-faults                                                 
     9.010183397                  0      page-faults                                                 
    10.011478644                  0      page-faults                                                 
    11.013237871                  0      page-faults                                                 
    12.014692430                  0      page-faults                                                 
    13.015920820                  0      page-faults                                                 
    14.017341534                  0      page-faults                                                 
    15.018367974                  0      page-faults                                                 
^C    15.666584881                  0      page-faults

看看,我观察时电脑没啥读写活动,所以只观察到了2次缺页异常。

3.4.1 使用 perf 跟踪 page fault

# perf record -e page-faults -a -g -- sleep 30
# perf script > out.stacks
# ./stackcollapse-perf.pl < out.stacks | ./flamegraph.pl --color=mem \
    --title="Page Fault Flame Graph" --countname="pages" > out.svg

如果对 major-faultsminor-faults 缺页异常感兴趣,也可以进行采样分析。

3.4.2 使用 ebpf 跟踪 page fault

# /usr/share/bcc/tools/stackcount 't:exceptions:page_fault_*' > out.stacks
# ./stackcollapse.pl < out.stacks | ./flamegraph.pl --color=mem \
    --title="Page Fault Flame Graph" --countname="pages" > out.svg

跟踪 page fault 的开销可能比跟踪 brk()mmap() 高一点,但幅度不大: page fault 应该仍然相对较少,这使得这种跟踪方法引入的开销几乎可以忽略不计。在实践中,page fault 是诊断内存增长和泄漏的廉价、快速且通常有效的方法。它无法说明一切,但值得一试。

4. 小结

本文介绍的这些内存跟踪方法,可以识别虚拟或物理内存的增长,并包括增长的所有原因,包括泄漏。跟踪 brk(),mmap()page fault 方法不能直接分别出内存泄漏,这需要进一步分析。但是,它们的优点是开销非常低,使其适合实时生产应用程序分析。这些方法的另一个优点是,通常无需重新启动应用程序即可部署跟踪工具。

5. 参考链接

https://brendangregg.com/FlameGraphs/memoryflamegraphs.html
https://www.brendangregg.com/perf.html#StackTraces
https://github.com/brendangregg/BPF-tools/tree/master/old/2017-12-23

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

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

相关文章

【FPGA】多功能ALU

目录 实验要求 源代码 顶层模块 数据输入模块 ALU运算模块 结果处理模块 扫描数码管模块 扫描数码管顶层 分频器 数码管显示 仿真代码 结构层图 管脚配置 实验板卡&#xff1a;xc7a100tlc sg324-2L&#xff0c;共20个开关 实验要求 通过高低位控制&#xff0c;实现32位数…

74-快速排序——一路快排

快速排序是影响二十世纪最伟大的排序算法之一。 JDK的双轴快速排序就是对快排的优化&#xff0c;本质还是快排。 从待排序区间选择一个数&#xff0c;作为基准值/分区点&#xff08;pivot&#xff09;&#xff0c;此时默认选择数组的第一个元素作为比较的基准值。partition&a…

【 SpringBoot 配置⽂件 】

文章目录一、配置⽂件作⽤二、配置文件格式2.1 特殊说明2.2 为配置⽂件安装提示插件三、properties 配置⽂件说明3.1 properties 基本语法3.2 读取配置⽂件3.3 解决 properties 中文乱码3.4 properties 缺点分析四、yml 配置⽂件说明4.1 yml 基本语法4.2 yml 配置读取4.3 yml 使…

前后端分离——SpringBoot+Vue——3天速成企业级项目

前后端分离——SpringBootVue使用到的技术&#xff1a;vue3&#xff08;区别vue2&#xff09;前言vue2 vs vue3双向绑定更新实例化生命周期获取props数据和方法的定义watchEffect那么什么是 watchEffect &#xff1f;组件通信注意attrs和listeners路由vue3路由写法&#xff1a;…

ROS学习——艰辛的环境安装之路一VMware

文章目录VMware 安装下载安装VMware 安装 一些没用的介绍&#xff1a; VMware Workstation中文版是一个“虚拟 PC”软件。它使你可以在一台机器上同时运行二个或更多 Windows、DOS、LINUX 系统。与“多启动”系统相比&#xff0c;VMWare 采用了完全不同的概念。多启动系统在一…

《Web前端应用开发》考试试卷(模拟题)

一、产品搜索页面 打开“考试文件夹”中的input.html&#xff0c;完成以下步骤&#xff1a; 注意&#xff1a;本题仅能在input.html的&#xff08;1&#xff09;为产品名称所在的div添加样式属性&#xff0c;使得产品名称保持在文本框的左边&#xff1b; &#xff08;2&#xf…

PPT NO.1【用ppt如何做一张海报+字体】

PPT做得好的人&#xff0c;一定是站在观众的角度思考的人。 1、设置幻灯片尺寸大小&#xff1a; 设置完成后如下&#xff1a; 2、加载一张自己喜欢的图片进来&#xff1a;【图片越高清越好】 将图片铺满空白的地方&#xff0c;调整好自己喜欢的区域&#xff1a; 做裁剪&#xf…

数据结构修炼第一篇:时间复杂度和空间复杂度

系列文章目录 第一章 时间复杂度和空间复杂度 第二章 顺序表&#xff0c;列表 第三章 栈和队列 第四章 二叉树 第五章 排序 目录 系列文章目录 &#x1f3c6;文章目录 &#x1f3c6;前言 &#x1f3c6;一、算法的复杂度 &#x1f3c6;二、时间复杂度的概念 大0渐进 作…

【Go语言从入门到精通系列-基础篇】Go安装 + 语言特性,以及开启你人生中的第一个go程序

系列文章 【Go语言从入门到精通系列-基础篇】Go安装 语言特性&#xff0c;以及开启你人生中的第一个go程序 【Go语言从入门到精通系列-基础篇】Go语言包的管理以及基础语法与使用。 Go语言从入门到精通系列-基础篇系列文章前言第一章 Go语言开发基础1.1 Go语言的优势1.2 Go语…

C语言快速排序非递归实现

目录 栈的辅助&#xff08;栈的实现可以调用之前实现的数据结构&#xff09;&#xff1a; 1&#xff0c;初始状态 2&#xff0c;循环 3&#xff0c;终止 4&#xff0c;注意 小点&#xff1a; 1&#xff0c;递归的使用会造成栈空间的消耗&#xff0c;使用递归&#xff0c;…

刷题day51:重新安排行程 ***

题意描述&#xff1a; 给你一份航线列表 tickets &#xff0c;其中 tickets[i] [fromi, toi] 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。 所有这些机票都属于一个从 JFK&#xff08;肯尼迪国际机场&#xff09;出发的先生&#xff0c;所以该行程必须从 …

【宝塔邮局管理器】使用教程、Email配置

1.安装宝塔邮局插件前&#xff0c;需要先安装redis服务&#xff0c;并设置redis密码。 安装完Redis服务后设置密码&#xff0c;设置密码时不要使用&%这类特殊符号 会导致负载状态显示异常&#xff0c;可使用英文数字组合密码 PS&#xff1a;邮局的反垃圾模块 rspamd服务需要…

Spring Cloud快速入门

文章目录Spring Cloud快速入门一、基础概念1、微服务架构2、微服务技术栈3、什么是Spring Cloud?4、Spring Cloud和Spring Boot的联系&#xff1f;5、比较成熟的互联网架构二、Rest环境搭建1、搭建提供者1.1、创建一个父工程1.2、创建一个springcloud-api模块1.3、创建一个spr…

SSM学习记录3:响应(注释方式 + SprigMVC项目 + 2022发布版本IDEA)

响应 ResponseBody注解的作用是将当前控制器中方法的返回值作为响应体 1.返回页面 无需在方法上进行ResponseBody注解&#xff0c;只需RequestMapping匹配地址&#xff0c;并且返回值为带后缀的页面名字符串 前面学习中除了json数据&#xff0c;所有带ResponseBody注解的方法…

iphone用什么蓝牙耳机好?和iphone适配的蓝牙耳机推荐

随着科技的不断发展&#xff0c;人们已经离不开各种智能设备。蓝牙耳机作为一种非常方便的音频设备&#xff0c;已经逐渐成为了许多人日常生活中不可或缺的一部分。然而&#xff0c;苹果产品的价格一直都是昂贵的&#xff0c;有没有与iphone适配的耳机呢&#xff1f;下面我们来…

ServletAPI详解(三)-HttpServletRequest

我们来看第二个类:HttpServletRequest HttpServletRequest HttpServletRequest表示的是一个http请求对象,是tomcat自动构造的,tomcat会实现监听端口,接收连接,读取请求,解析请求,构造请求对象等一系列操作 下面的方法可用在 Servlet 程序中读取 HTTP 头。这些方法通过 HttpS…

若依— — 快速入门【源码分析】

若依— — 快速入门 1 什么是若依 官网地址&#xff1a;http://www.ruoyi.vip/ 若依是一款优秀的开源项目&#xff0c;涉及到企业开发中大部分的管理系统&#xff0c;我们依此为模板进行二次开发&#xff0c;可以快速开发出符合大部分公司中的后台管理系统。 2 使用若依 使用开…

Spring Security --- authorizeRequests配置

目录 自定义配置类之访问权限 匹配顺序规则 访问控制包含 访问控制url匹配 访问控制方法 角色、权限判断 使用注解进行角色权限控制 自定义配置类之访问权限 http.authorizeRequests()主要是对url进行访问权限控制通过这个方法来实现url授权操作支持链式写法 匹配顺序…

【react全家桶学习】react简介

react是什么&#xff1f; react是用于构建用户界面的JS库&#xff0c;是一个将数据渲染为HTML视图的开源JS库 谁开发的&#xff1f; 由Facebook开发&#xff0c;且开源 为什么要学&#xff1f; 原生JavaScript操作DOM繁琐、效率低 ( DOM-API操作 UI)使用JavaScript直接操作…

Attention注意力机制

加粗样式通俗理解&#xff1a;你会注意什么&#xff1f; 对于一个模型而言&#xff08;CNN&#xff0c;LSTM&#xff09;&#xff0c;模型本身很难决定什么重要什么不重要&#xff0c;因此注意力机制诞生了。 注意力机制&#xff1a;我们会把焦点聚焦在比较重要的事务上 怎么…