通过分叉减轻内存泄漏
Mitigating memory leaks by forking
这真是个丑陋的问题。
我有一个 C++ 程序,它循环执行以下操作:
- 等待 JMS 消息
- 计算一些数据
- 发送 JMS 消息作为响应
我的程序(姑且称之为"Bob")有相当严重的内存泄漏。内存泄漏位于别人编写的共享库中,我必须使用它,但我无权访问源代码。
此内存泄漏导致 Bob 在循环的 "calculates some data" 阶段崩溃。这是一个问题,因为另一个程序正在等待 Bob 的响应,如果没有收到响应,它会非常沮丧。
由于各种限制(是的,这是一个 X/Y 问题,我告诉过你它很丑),我确定我唯一可行的策略是修改 Bob,使其在其循环:
- 等待 JMS 消息
- 计算一些数据
- 发送 JMS 消息作为响应
- 检查是否有使用"too much"内存的危险
- 如果是,派生并执行自身的另一个副本,然后优雅地退出
我的问题如下:
检测我们是否正在使用 "too much" 内存的最佳(可靠但不太低效)方法是什么?我目前的想法是比较getrlimit(RLIMIT_AS) rlim_cur
和getrusage(RUSAGE_SELF) ru_maxrss
;那是对的吗?如果没有,什么是更好的方法? Bob 在各种主机上的 Linux VM 中运行,所有主机都具有不同的内存量。
如果您让程序本身重新启动或将 "Calculates some data" 部分分叉到一个单独的进程,无论如何您都需要检查内存消耗。由于您在 Linux,检查这一点的一个简单方法是获取感兴趣进程的 pid 号,然后读取文件 /proc/$PID/statm
的内容。第二个数字是常驻集的大小。
读取这些 proc 文件是 top 和 htop 等工具获取进程数据的方式。定期读取一个 ~30 字节的 in-memory 文件来检查内存泄漏听起来效率不高。
如果泄漏是有规律的并且您想让它更复杂一些,您甚至可以跟踪增长率并相应地调整您的检查率。
假设内存泄漏发生在 "Calculates some data" 阶段,我认为将那部分重构为一个单独的程序并在其自己的进程中执行它可能更有意义。这样,您至少可以隔离有问题的代码并使其在将来更容易替换它,而不是仅仅通过让程序在内存不足时自行重启来掩盖问题。
"Calculates some data" 部分可以是 long-running 进程,等待来自主程序的请求并在必要时自行重启,或者(更简单)它可以是 one-and-done只在 *argv
中获取数据并将其结果发送到 stdout
的程序。然后你的主循环每次都可以分叉并执行它,并在它们返回时读取结果。如果可能的话,我会选择更简单的选项,但这当然取决于您的需求。
这真是个丑陋的问题。
我有一个 C++ 程序,它循环执行以下操作:
- 等待 JMS 消息
- 计算一些数据
- 发送 JMS 消息作为响应
我的程序(姑且称之为"Bob")有相当严重的内存泄漏。内存泄漏位于别人编写的共享库中,我必须使用它,但我无权访问源代码。
此内存泄漏导致 Bob 在循环的 "calculates some data" 阶段崩溃。这是一个问题,因为另一个程序正在等待 Bob 的响应,如果没有收到响应,它会非常沮丧。
由于各种限制(是的,这是一个 X/Y 问题,我告诉过你它很丑),我确定我唯一可行的策略是修改 Bob,使其在其循环:
- 等待 JMS 消息
- 计算一些数据
- 发送 JMS 消息作为响应
- 检查是否有使用"too much"内存的危险
- 如果是,派生并执行自身的另一个副本,然后优雅地退出
我的问题如下:
检测我们是否正在使用 "too much" 内存的最佳(可靠但不太低效)方法是什么?我目前的想法是比较getrlimit(RLIMIT_AS) rlim_cur
和getrusage(RUSAGE_SELF) ru_maxrss
;那是对的吗?如果没有,什么是更好的方法? Bob 在各种主机上的 Linux VM 中运行,所有主机都具有不同的内存量。
如果您让程序本身重新启动或将 "Calculates some data" 部分分叉到一个单独的进程,无论如何您都需要检查内存消耗。由于您在 Linux,检查这一点的一个简单方法是获取感兴趣进程的 pid 号,然后读取文件 /proc/$PID/statm
的内容。第二个数字是常驻集的大小。
读取这些 proc 文件是 top 和 htop 等工具获取进程数据的方式。定期读取一个 ~30 字节的 in-memory 文件来检查内存泄漏听起来效率不高。
如果泄漏是有规律的并且您想让它更复杂一些,您甚至可以跟踪增长率并相应地调整您的检查率。
假设内存泄漏发生在 "Calculates some data" 阶段,我认为将那部分重构为一个单独的程序并在其自己的进程中执行它可能更有意义。这样,您至少可以隔离有问题的代码并使其在将来更容易替换它,而不是仅仅通过让程序在内存不足时自行重启来掩盖问题。
"Calculates some data" 部分可以是 long-running 进程,等待来自主程序的请求并在必要时自行重启,或者(更简单)它可以是 one-and-done只在 *argv
中获取数据并将其结果发送到 stdout
的程序。然后你的主循环每次都可以分叉并执行它,并在它们返回时读取结果。如果可能的话,我会选择更简单的选项,但这当然取决于您的需求。