记一次 .NET某游戏币自助机后端 内存暴涨分析
一:背景
1. 讲故事
前些天有位朋友找到我,说他们的程序内存会偶发性暴涨,自己分析了下是非托管内存问题,让我帮忙看下怎么回事?哈哈,看到这个dump我还是非常有兴趣的,居然还有这种游戏币自助机类型的程序,下次去大玩家看看他们出币的机器后端是不是C#写的?由于dump是linux上的程序,刚好windbg可以全平台分析,太爽了,直接用windbg开干吧。
二:WinDbg 分析
1. 到底是哪里的泄漏
在 windows 平台上相信有很多朋友都知道用 !address -summary 命令看,但这是专属于windows平台的命令,在分析linux上的dump不好使,参考如下输出:
0:000> !address -summary--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
<unknown> 1685 7ffc`d6725c00 ( 127.988 TB) 100.00% 100.00%
Image 7102 0`0b524400 ( 181.142 MB) 0.00% 0.00%--- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal2248 7ffc`02549000 ( 127.984 TB) 100.00%
MEM_PRIVATE 6539 0`df701000 ( 3.491 GB) 0.00% 0.00%--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal2248 7ffc`02549000 ( 127.984 TB) 100.00% 100.00%
MEM_COMMIT 6539 0`df701000 ( 3.491 GB) 0.00% 0.00%--- Protect Summary (for commit) - RgnCount ----------- Total Size -------- %ofBusy %ofTotal
PAGE_READWRITE 2099 0`dd75e000 ( 3.460 GB) 0.00% 0.00%
PAGE_EXECUTE_WRITECOPY 33 0`00d4c000 ( 13.297 MB) 0.00% 0.00%
PAGE_READONLY 2736 0`00b01000 ( 11.004 MB) 0.00% 0.00%
PAGE_EXECUTE_READ 1671 0`00756000 ( 7.336 MB) 0.00% 0.00%--- Largest Region by Usage ----------- Base Address -------- Region Size ----------
<unknown> 0`00000000 55cb`2dc3b000 ( 85.794 TB)
Image 7f71`9dbdd000 0`01b16000 ( 27.086 MB)
卦中的内存段分类用处不大,也没有多大的参考价值,那怎么办呢?其实 coreclr 团队也考虑到了这个情况,它提供了一个 maddress 命令来实现跨平台的 !address,更改后输出如下:
0:000> !sos maddress
Enumerating and tagging the entire address space and caching the result...
Subsequent runs of this command should be faster.
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Memory Kind | StartAddr | EndAddr-1 | Size | Type | State | Protect | Image | +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Stack | 7f6e356ec000 | 7f6e35eec000 | 8.00mb | MEM_PRIVATE | MEM_COMMIT | PAGE_READWRITE | | | Stack | 7f6e35eed000 | 7f6e366ed000 | 8.00mb | MEM_PRIVATE | MEM_COMMIT | PAGE_READWRITE | | | Stack | 7f6e366ee000 | 7f6e36eee000 | 8.00mb | MEM_PRIVATE | MEM_COMMIT | PAGE_READWRITE | | | Stack | 7f6e36eef000 | 7f6e376ef000 | 8.00mb | MEM_PRIVATE | MEM_COMMIT | PAGE_READWRITE | | ...+-------------------------------------------------------------------------+ | Memory Type | Count | Size | Size (bytes) | +-------------------------------------------------------------------------+ | Stack | 423 | 3.29gb | 3,528,859,648 | | Image | 7,102 | 181.14mb | 189,940,736 | | PAGE_READWRITE | 206 | 89.18mb | 93,511,680 | | GCHeap | 3 | 37.75mb | 39,587,840 | | HighFrequencyHeap | 395 | 24.66mb | 25,858,048 | | LowFrequencyHeap | 316 | 22.20mb | 23,277,568 | | LoaderCodeHeap | 13 | 17.00mb | 17,825,792 | | ResolveHeap | 2 | 732.00kb | 749,568 | | HostCodeHeap | 8 | 668.00kb | 684,032 | | DispatchHeap | 1 | 196.00kb | 200,704 | | PAGE_EXECUTE_WRITECOPY | 6 | 184.00kb | 188,416 | | CacheEntryHeap | 3 | 164.00kb | 167,936 | | IndirectionCellHeap | 3 | 152.00kb | 155,648 | | LookupHeap | 3 | 144.00kb | 147,456 | | StubHeap | 2 | 76.00kb | 77,824 | | PAGE_EXECUTE_READ | 1 | 4.00kb | 4,096 | +-------------------------------------------------------------------------+ | [TOTAL] | 8,487 | 3.65gb | 3,921,236,992 | +-------------------------------------------------------------------------+
从卦中可以看到当前程序总计 3.65G 内存占用,基本上都被线程栈给吃掉了,更让人意想不到的是这个线程栈居然占用 8M 的内存空间,这个着实有点大了,而且 linux 不像 windows 有一个 reserved 的概念,这里的 8M 是实实在在的预占,可以观察这 8M 的内存地址即可,都是初始化的 0, 这就说不过去了。
0:000> dp 7f6e356ec000 7f6e35eec000
00007f6e`356ec000 00000000`00000000 00000000`00000000
...
00007f6e`35eebfc0 00000000`00000000 00000000`00000000
00007f6e`35eebfd0 00000000`00000000 00000000`00000000
00007f6e`35eebfe0 00000000`00000000 00000000`00000000
00007f6e`35eebff0 00000000`00000000 00000000`00000000
2. 如何修改栈空间大小
一般来说不同的操作系统发行版有不同的默认栈空间配置,可以先到内存搜一下当前是哪一个发行版,做法就是搜索操作系统名称主要关键字。
0:000> s-a 0 L?0xffffffffffffffff "centos"
...
000055cb`2ecf08c8 63 65 6e 74 6f 73 2e 37-2d 78 36 34 00 00 00 00 centos.7-x64....
...
从卦中可以看到当前操作系统是 centos7-x64,在 windows 平台上修改栈空间大小可以修改 PE 头,在 linux 上有两种做法。
- 修改 ulimit -s 参数
root@ubuntu:/data# ulimit -s
8192
root@ubuntu:/data# ulimit -s 2048
root@ubuntu:/data# ulimit -s
2048
- 修改 DOTNET_DefaultStackSize 环境变量
DOTNET_DefaultStackSize=180000
更多可以参考文章: https://www.alexander-koepke.de/post/2023-10-18-til-dotnet-stack-size/
上面是解决问题的第一个方向,接下来我们说另一个方向,为什么会产生总计 423 个线程呢?
3. 为什么会有那么多线程
要找到这个答案,需要去看每一个线程此时都在干嘛,这个可以使用 windbg 专属命令。
0:000> ~*e !clrstack
...
OS Thread Id: 0x4e (24)Child SP IP Call Site
00007F70B20FC4B0 00007f71a4131ad8 [InlinedCallFrame: 00007f70b20fc4b0] /app/Confluent.Kafka.dll!Unknown
00007F70B20FC4B0 00007f7130299970 [InlinedCallFrame: 00007f70b20fc4b0] /app/Confluent.Kafka.dll!Unknown
00007F70B20FC4A0 00007f7130299970 ILStubClass.IL_STUB_PInvoke(IntPtr, IntPtr)
00007F70B20FC530 00007f7130309fab /app/Confluent.Kafka.dll!Unknown
00007F70B20FC880 00007f7131c5a75d /app/Confluent.Kafka.dll!Unknown
00007F70B20FC8A0 00007f7130303ebe /app/DotNetCore.CAP.Kafka.dll!Unknown
00007F70B20FC980 00007f71302f4854 /app/DotNetCore.CAP.dll!Unknown
00007F70B20FCA50 00007f7129b187f4 System.Threading.Tasks.Task.InnerInvoke() [/_/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs @ 2387]
00007F70B20FCA70 00007f7129b1d316 System.Threading.Tasks.Task+c.<.cctor>b__272_0(System.Object) [/_/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs @ 2375]
00007F70B20FCA80 00007f7129b03d6b System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) [/_/src/libraries/System.Private.CoreLib/src/System/Threading/ExecutionContext.cs @ 183]
00007F70B20FCAD0 00007f7129b18524 System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef, System.Threading.Thread) [/_/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs @ 2333]
00007F70B20FCB50 00007f7129b18418 System.Threading.Tasks.Task.ExecuteEntryUnsafe(System.Threading.Thread) [/_/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs @ 2271]
00007F70B20FCB70 00007f7129b21a67 System.Threading.Tasks.ThreadPoolTaskScheduler+c.<.cctor>b__10_0(System.Object) [/_/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ThreadPoolTaskScheduler.cs @ 35]
00007F70B20FCB80 00007f7129af88c2 System.Threading.Thread.StartCallback() [/_/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @ 105]
00007F70B20FCCF0 00007f71a37ab9c7 [DebuggerU2MCatchHandlerFrame: 00007f70b20fccf0]
...
从卦中数据看有很多的 Unknown,说明dump取得不好,可能不是用正规的 dotnet-dump 或者 procdump,但不管怎么说,还是可以看到大量的和 Kafka 有关的链接库,并且从 InnerInvoke 这个执行 m_action 来看,应该是有大量线程卡在 Kafka 中的某个函数上。
有了这些知识,最后给到朋友的建议如下:
- 修改 DOTNET_DefaultStackSize 参数
可以仿照 windows 上的 .netcore 默认 1.5M 的栈空间设置,因为8M真的太大了,扛不住,也和 Linux 的低内存使用不符。
- 观察 Kafka 的相关逻辑
毕竟有大量线程在 Kafka 的等待上,个人觉得可能是订阅线程太多,或者什么业务执行时间长导致的线程饥饿,尽量把线程压下去。
三:总结
Linux 上的 .NET 调试生态在日渐丰富,这是一件让人很兴奋的事情,最后再给 WinDbg 点个赞,它不仅可以全平台dump分析,还可以实时调试 Linux 进程,现如今的WinDbg真的是神一般的存在。
相关文章:
记一次 .NET某游戏币自助机后端 内存暴涨分析
一:背景 1. 讲故事 前些天有位朋友找到我,说他们的程序内存会偶发性暴涨,自己分析了下是非托管内存问题,让我帮忙看下怎么回事?哈哈,看到这个dump我还是非常有兴趣的,居然还有这种游戏币自助机…...
计算机考研|哪些985/211院校不歧视双非二本生?
说句残酷的话,能对某一专业做出贡献,那么你的水平肯定是很高的。如果普通学生,普通本科的话可能很难做到这一点。这也是现在考研风气比较强的原因,一部分专业能力不突出的学生来选择深造3年。 对于基础较差想要考计算机研究生的同…...
Spring Boot:简化 Java 应用开发的艺术
Spring Boot 是一种用于快速开发、运行和管理 Java 应用程序的开源框架。它简化了基于 Spring 的应用程序的配置和部署,使得开发者能够更加专注于业务逻辑的实现。本文将介绍 Spring Boot 的核心特性、优势以及如何在项目中使用 Spring Boot。 一、核心特性 自动配…...
elasticsearch安装与使用(2)-基于term匹配的简单搜索引擎搭建
把一篇pdf论文解析后,放入es数据库中,建立倒排索引表,并实现简单搜索。 1、pdf论文解析(英文) 安装pdf解析包 pip install pdfminer.sixdef extract_text_from_pdf(filename, page_numbersNone, min_line_length1):从pdf文件中提取文字:pa…...
速盾:ddos防护与高防ip区别?
在网络安全领域,DDOS 防护和高防 IP 都是重要的防护手段,但它们之间存在着一些明显的区别。 DDOS 防护是一种针对分布式拒绝服务攻击的防御策略。它通过多种技术和方法来识别和抵御 DDOS 攻击。常见的 DDOS 防护手段包括流量清洗、连接限制、协议分析等。…...
Java中StringBulider详解
StringBuilder 是 Java 中一个用来创建可变字符串的类。与 String 类不同,StringBuilder 对象能够被修改,不会创建新的对象,因此在需要进行大量字符串操作时,StringBuilder 更高效。以下是 StringBuilder 的详细讲解。 创建 Stri…...
基于springboot高校就业招聘系统的设计
管理员账户功能包括:系统首页,个人中心,就业咨询管理,毕业去向管理,简历管理,管理员管理,基础数据管理 辅导员账户功能包括:系统首页,个人中心,就业咨询管理…...
嵌入式C语言编码规范要点
1.函数命名方法 骆驼命名法(Camel) 帕斯卡命名法((pascal),也叫大驼峰命名法(Upper Camel Case) 匈牙利命名法 下划线命名法(也称为蛇形命名法) 详述见之前文…...
Python中的全局解释器锁:深入解析与应对策略
1. 引言 在Python的世界里,全局解释器锁(GIL)是一个经常被讨论的话题。它既是Python并发编程中的一个重要概念,也是许多开发者感到困惑的源头。本文将深入探讨GIL的工作原理、它对Python程序性能的影响 2. 全局解释器锁的历史背…...
【java计算机毕设】图书商城管理系统MySQL springboot vue html maven送文档
1项目功能介绍 【java计算机毕设】图书商城管理系统 Java Spring Boot vue HTML MySQL 赠送文档 PPT 2项目简介 系统功能: 图书商城管理系统包括管理员和用户两种角色。 管理员的功能包括在个人中心修改个人信息,以及在基础数据管理中管理会员等级类型和…...
【Java刷题】二叉树
相同的树 public boolean isSameTree(TreeNode p, TreeNode q) {if(p null && q null) {return true;} else if(p ! null && q ! null) {if(p.val ! q.val) {return false;} else {return isSameTree(p.left, q.left) && isSameTree(p.right, q.rig…...
【Linux】程序地址空间之动态库的加载
我们先进行一个整体轮廓的了解,随后在深入理解细节。 在动态库加载之前还要说一下程序的加载,因为理解了程序的加载对动态库会有更深的理解。 轮廓: 首先,不管是程序还是动态库刚开始都是在磁盘中的,想要执行对应的可…...
LabVIEW处理大量数据时,怎样确保数据的准确性和完整性?
在LabVIEW处理中,确保大量数据的准确性和完整性至关重要。以下是详细的多角度分析和建议,以确保在LabVIEW中处理大量数据时,数据的准确性和完整性: 1. 数据采集阶段 1.1 高精度硬件选择 选择高精度的数据采集硬件,如…...
容器是什么?
概念 容器可以被看作是一种轻量级的虚拟化技术。与传统虚拟化技术相比,容器不需要为每个应用程序提供单独的操作系统,它们共享宿主机的操作系统内核。这使得容器更加轻便和高效。 想象一下,容器就像是一艘艘可以在海洋中独立航行的货轮&…...
#15 从Stable Diffusion生成的艺术中寻找灵感
文章目录 前言1. Stable Diffusion简介2. 寻找灵感的途径2.1 深入探索主题2.2 结合多种艺术风格2.3 实验不同的创意组合 3. 灵感应用3.1 艺术创作3.2 设计项目3.3 故事讲述 4. 实践建议4.1 记录和迭代4.2 开放实验4.3 结合个人风格 结论 前言 在当今的数字时代,人工…...
git rebase
1. git rebase的意义 首先理解这个rebase,它的意思是re base,翻译过来就是“重新基于”。 意义是:重新整理当前分支的开发线,使其变成基于某个开发节点的开发线。 2. rebase用于并行开发 构造两个分支master和feature…...
Docker引起的漏洞问题
前言 测试环境上的中间件和java应用都是由docker进行部署的,但是因为docker的镜像访问有时候需要外网,由此引发了问题,在docker文件中 /usr/lib/systemd/system/docker.service 原有的配置为,可以看到进行了加密 ExecStart/usr/bin/dockerd --tlsverify --tlscacert/etc/docker…...
Oracle基本数据类型
在Oracle数据库中,数据类型是描述数据存储格式的属性。不同的数据类型允许存储不同种类的数据。以下是Oracle中的一些基本数据类型: 1. 字符数据类型 - CHAR(size): 定长字符数据,最大长度为2000字节。 - VARCHAR2(size): 变长字符数据…...
VS+QT+OCC创建坐标界面
1、安装并配置好项目后,填写如下代码: #pragma once#include <Standard_Handle.hxx> #include <V3d_Viewer.hxx> #include <OpenGl_GraphicDriver.hxx> #include <WNT_Window.hxx> #include <V3d_View.hxx> #include <…...
VUE2.7项目配置webpack打包-详细操作步骤
一、Webpack简介 Webpack是一个打包工具,可以把JS、CSS、Node Module、Coffeescrip、SCSS/LESS、图片等都打包在一起,因此,现在几乎所有的SPA项目、JS项目都会用到Webpack。 官网:https://webpack.js.org GitHub为https://git…...
装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...
jmeter聚合报告中参数详解
sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample(样本数) 表示测试中发送的请求数量,即测试执行了多少次请求。 单位,以个或者次数表示。 示例:…...
【Linux系统】Linux环境变量:系统配置的隐形指挥官
。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量:setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...
c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...
uniapp 集成腾讯云 IM 富媒体消息(地理位置/文件)
UniApp 集成腾讯云 IM 富媒体消息全攻略(地理位置/文件) 一、功能实现原理 腾讯云 IM 通过 消息扩展机制 支持富媒体类型,核心实现方式: 标准消息类型:直接使用 SDK 内置类型(文件、图片等)自…...
电脑桌面太单调,用Python写一个桌面小宠物应用。
下面是一个使用Python创建的简单桌面小宠物应用。这个小宠物会在桌面上游荡,可以响应鼠标点击,并且有简单的动画效果。 import tkinter as tk import random import time from PIL import Image, ImageTk import os import sysclass DesktopPet:def __i…...
如何做好一份技术文档?从规划到实践的完整指南
如何做好一份技术文档?从规划到实践的完整指南 🌟 嗨,我是IRpickstars! 🌌 总有一行代码,能点亮万千星辰。 🔍 在技术的宇宙中,我愿做永不停歇的探索者。 ✨ 用代码丈量世界&…...
