linux下程序占用内存只涨不降原因追查

本文记录了在Linux环境下进行战斗校验压测过程中遇到的内存占用持续上升问题及其解决方法。通过使用valgrind排除内存泄露后,借助strace定位到brk系统调用导致的问题,并最终通过调用malloc_trim(0)解决了内存占用居高不下的难题。
部署运行你感兴趣的模型镜像

        这几天在做战斗校验压测的时候发现,战斗校验进程占用内存一直在涨,而且长时间不降,但是相同逻辑在windows下跑的时候,校验结束进程占用内存就会降下来,这里记录一下整个追查过程。

        首先怀疑有内存泄露,于是先用valgrind

valgrind --tool=memcheck --leak-check=full --log-file=/tmp/bc_leak ./battle_check

校验几场场战斗后得到结果并没有"definitely lost", "indirectly lost", "possibly lost", "still reachable", and "suppressed"这五种级别的泄露提示,据此判断,应该是没有内存泄露。 

         根据上面的检测结果,分析一下,没有泄露,但是进程占用内存又一直在增加,那应该是从系统申请了内存但是没有还回去,那什么操作会导致这样的结果呢,继续追查,这次该拿出系统调用追踪利器strace

strace -tt -f -o /tmp/bc_leak_strace ./battle_check

同时结合top命令

top -p 17626 -d 0.1

这样可以观察在进程res占用增大的时候,系统调用是什么,得到的数据是这样的,在res猛增的时候,一直在跑brk调用   

 

而后是没有对应的释放操作的,于是上网查了一下brk申请内存导致只涨不降的情况,发现还挺多的,主要是glibc的优化,brk比mmap性能好,小内存是brk申请,大内存就是mmap申请,但是brk申请的内存回收有个限制,如果最后申请的内存还在用的话,之前所有不在用的内存都不能回收,除非用malloc_trim(0)强制回收,找到症结之后,在每场校验最后加了强制回收空闲内存的逻辑,

 

这会触发之前大量的内存被回收,在我的逻辑中,内存从250m增长到460m,然后一次全部回收完,校验进程占用的内存会回到最原始的250m。

这里贴一下相关文章,单价可以参考:

glibc下的内存管理 - lookof - 博客园

内存泄漏之malloc_trim - 寒魔影 - 博客园

您可能感兴趣的与本文相关的镜像

ACE-Step

ACE-Step

音乐合成
ACE-Step

ACE-Step是由中国团队阶跃星辰(StepFun)与ACE Studio联手打造的开源音乐生成模型。 它拥有3.5B参数量,支持快速高质量生成、强可控性和易于拓展的特点。 最厉害的是,它可以生成多种语言的歌曲,包括但不限于中文、英文、日文等19种语言

### 变量值在未主动修改情况下发生改变的可能原因Linux 内核中,变量值在没有显式修改的情况下发生变化,可能由以下几个原因导致: 1. **并发访问与竞态条件(Race Condition)** 多个线程或中断处理程序同时访问和修改共享变量,未进行同步保护,导致变量值被意外更改。例如,在中断上下文和进程上下文中同时修改一个全局变量,而没有使用锁机制,就可能导致值的异常变化。 2. **内存映射或指针错误** 如果变量的地址被其他模块映射或通过指针间接修改,也可能导致值发生变化。例如,通过 `ioremap` 映射的寄存器地址与变量地址冲突,或指针误操作覆盖了变量所在的内存区域。 3. **编译器优化** 编译器在优化过程中可能对变量的访问顺序或存储方式进行调整,导致在调试器中观察到的值与预期不符。例如,使用 `-O2` 或更高优化级别时,编译器可能会将变量缓存到寄存器中,而不是每次都从内存读取。 4. **内存损坏(Memory Corruption)** 堆栈溢出、缓冲区越界写入或内存分配错误可能导致变量所在的内存区域被覆盖。例如,使用 `strcpy()` 而未检查长度限制,可能覆盖相邻变量的数据。 5. **内核模块或驱动程序的副作用** 第三方内核模块或驱动程序可能在加载或运行过程中修改了某些全局变量,尤其是在未遵循良好封装原则的情况下。 6. **定时器或异步事件触发** 内核中存在大量异步操作,如定时器回调、workqueue 或 tasklet,这些机制可能在后台修改变量值,而开发人员未意识到其影响范围。 7. **调试器或监控工具干扰** 使用某些调试工具(如 `gdb` 或 `perf`)时,可能会无意中修改变量值,尤其是在设置断点或内存访问监控时。 --- ### 追查方法与调试策略 1. **使用 `kprobe` 或 `ftrace` 跟踪变量访问** 可以通过 `kprobe` 设置内存访问断点,监控变量的读写行为。例如: ```bash echo 'p:var_access symbol_name+offset' > /sys/kernel/debug/tracing/kprobe_events echo 1 > /sys/kernel/debug/tracing/events/kprobes/var_access/enable cat /sys/kernel/debug/tracing/trace_pipe ``` 这样可以捕获所有访问该变量的调用栈,帮助定位修改源。 2. **启用 `KASAN`(Kernel Address Sanitizer)检测内存错误** `KASAN` 是一种动态检测内存错误的工具,可以发现越界访问、使用已释放内存等问题。启用后,当变量所在内存区域被非法访问时,内核会输出详细的错误信息。 3. **使用 `objdump` 或 `gdb` 分析变量地址** 通过反汇编工具查看变量在内存中的确切位置,并检查是否有其他函数或模块访问该地址。例如: ```bash objdump -t vmlinux | grep variable_name ``` 4. **启用 `CONFIG_DEBUG_ATOMIC_SLEEP` 和 `CONFIG_DEBUG_LOCK_ALLOC`** 如果怀疑是并发问题,启用这些配置选项可以帮助检测在原子上下文中睡眠或锁分配错误,从而定位潜在的竞态条件。 5. **添加日志输出** 在变量被修改的可疑位置添加 `pr_info()` 或 `printk()`,记录调用栈和上下文信息。例如: ```c pr_info("Variable modified at %pS\n", __builtin_return_address(0)); ``` 6. **使用 `crash` 工具分析内核崩溃转储** 如果变量的变化导致了内核崩溃,可以使用 `crash` 工具分析 `vmcore` 文件,查看当时的内存状态和调用栈。 7. **静态代码分析** 使用 `sparse` 或 `Coccinelle` 等工具进行静态分析,查找潜在的指针错误、类型不匹配或内存泄漏问题。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值