如何查找我是不是有内存泄漏

Posted

技术标签:

【中文标题】如何查找我是不是有内存泄漏【英文标题】:How to find if I have a memory leak如何查找我是否有内存泄漏 【发布时间】:2015-05-18 15:34:14 【问题描述】:

我写了一个数字运算算法。这个想法是:

    小型主程序需要很少的内存(从 2 MB 开始) 然后,在一个循环中,它调用一个需要相当多内存(大约 100 MB)的函数,当函数结束时应该释放这些内存。为了了解发生了什么,现在总是使用相同的参数调用该函数。

似乎程序慢慢吃掉了内存,所以我怀疑是内存泄漏。我尝试过 Clang 的 Address Sanitizer 和 Intel 的 Pointer Checker,但他们什么也没找到。

现在,我正在查看我的 Activity Monitor 中的内存消耗(我正在运行 OSX,但我从 Unix 命令“top”得到相同的内存使用),就在调用大函数之前,程序需要 2兆。运行该函数时,程序占用 120 MB。奇怪的是,当程序结束大函数并返回循环时,它现在占用了 37 MB!然后,当它回到大函数时,它需要 130 MB。同样,回到循环中,它需要 36 MB,然后在大函数中需要 140 MB...

所以它正在慢慢消失,但不是以常规模式。我应该如何信任“top”中的内存使用情况?

内存碎片是否可以增加内存使用而不发生内存泄漏?


我让程序运行一夜,这是我得到的数据:

    在第一个循环中,程序占用 150 MB 2 小时后,经过 68 次循环,程序占用 220 MB 经过一晚和 394 次循环,程序占用 480 MB

因此,分配和取消分配内存(大约 120 MB)的函数似乎在每次调用时“泄漏”了 1 MB。

【问题讨论】:

使用 RAII 怎么样? 你试过valgrind “内存碎片可以增加内存使用率而不会发生内存泄漏吗?” 可以。更重要的是,释放内存并不意味着您的操作系统必须立即将其从正在运行的进程中清除。 “出于各种原因。其中之一是我使用无符号整数作为索引” std::vector 也是如此。 “我有一个调试模式,将所有 il::Vector 初始化为 NaN” 通过 包装 std::vector 来完成,而不是完全替换它。 @tobi303 它可靠地测量了 something,但那是操作系统保留的内存,而不是程序分配的内存。 【参考方案1】:

首先,确保在很长一段时间内(例如,如果一个迭代需要一分钟,运行几个小时),增长会持续下去。如果生长渐近线那么没有问题。接下来我会尝试valgrind。然后,如果这没有帮助,您将不得不对您的代码进行二进制搜索:注释掉位,直到增长停止。我将首先完全删除 MKL 库的使用(如果您愿意,请留下存根)并看看会发生什么。接下来,将您的向量更改为std::vector,看看是否有帮助。之后,您将不得不使用自己的判断力。

【讨论】:

非常好的建议。我已经删除了 MKL,并且似乎内存进入了渐近线。还在努力…… 问题可能已解决。在发布问题之前,我已经在 std::unordered_map 中解决了“我写的越界”的错误。修复这个问题后,我认为问题仍然存在,因为在第一次迭代中内存仍在缓慢增长。正如您所建议的那样,似乎内存碎片不会使使用的内存保持不变,而是使其收敛到渐近线。我运行了很长时间,这就是现在似乎发生的事情。我会让它在一夜之间运行,看看会发生什么......【参考方案2】:

我认为我找到了罪魁祸首:MKL(截至今天的最新版本)。我使用 Pardiso,以下示例泄漏非常缓慢:每 13 秒大约 0.1 MB,这导致一夜之间 280 MB。这些是我从模拟中得到的数字。

如果你想试一试,你可以编译它:

icpc -std=c++11 pardiso-leak.cpp -o main -lmkl_intel_lp64 -lmkl_core -lmkl_intel_thread -liomp5 -ldl -lpthread -lm

感谢大家的帮助。我已经向英特尔报告了这个错误。

#include <iostream>
#include <vector>

#include "mkl_pardiso.h"
#include "mkl_types.h"

int main (int argc, char const *argv[])

  const auto n = std::size_t1000;
  auto m = MKL_INTn * n;

  auto values = std::vector<double>();
  auto column = std::vector<MKL_INT>();
  auto row = std::vector<MKL_INT>();

  row.push_back(1);
  for(std::size_t j = 0; j < n; ++j) 
    column.push_back(j + 1);
    values.push_back(1.0);
    column.push_back(j + n + 1);
    values.push_back(0.1);
    row.push_back(column.size() + 1);
  
  for(std::size_t i = 1; i < n - 1; ++i) 
    for(std::size_t j = 0; j < n; ++j) 
      column.push_back(n * i + j - n + 1);
      values.push_back(0.1);
      column.push_back(n * i + j + 1);
      values.push_back(1.0);
      column.push_back(n * i + j + n + 1);
      values.push_back(0.1);
      row.push_back(column.size() + 1);
    
  
  for(std::size_t j = 0; j < n; ++j) 
    column.push_back((n - 1) * n + j - n + 1);
    values.push_back(0.1);
    column.push_back((n - 1) * n + j + 1);
    values.push_back(1.0);
    row.push_back(column.size() + 1);
  

  auto y = std::vector<double>(m, 1.0);
  auto x = std::vector<double>(m, 0.0);

  auto pardiso_nrhs = MKL_INT1;
  auto pardiso_max_fact = MKL_INT1;
  auto pardiso_mnum = MKL_INT1;
  auto pardiso_mtype = MKL_INT11;
  auto pardiso_msglvl = MKL_INT0;
  MKL_INT pardiso_iparm[64];
  for (int i = 0; i < 64; ++i) 
    pardiso_iparm[i] = 0;
  
  pardiso_iparm[0] = 1;
  pardiso_iparm[1] = 2;
  pardiso_iparm[3] = 0;
  pardiso_iparm[4] = 0;
  pardiso_iparm[5] = 0;
  pardiso_iparm[7] = 0;
  pardiso_iparm[8] = 0;
  pardiso_iparm[9] = 13;
  pardiso_iparm[10] = 1;
  pardiso_iparm[11] = 0;
  pardiso_iparm[12] = 1;
  pardiso_iparm[17] = -1;
  pardiso_iparm[18] = 0;
  pardiso_iparm[20] = 0;
  pardiso_iparm[23] = 1;
  pardiso_iparm[24] = 0;
  pardiso_iparm[26] = 0;
  pardiso_iparm[27] = 0;
  pardiso_iparm[30] = 0;
  pardiso_iparm[31] = 0;
  pardiso_iparm[32] = 0;
  pardiso_iparm[33] = 0;
  pardiso_iparm[34] = 0;
  pardiso_iparm[59] = 0;
  pardiso_iparm[60] = 0;
  pardiso_iparm[61] = 0;
  pardiso_iparm[62] = 0;
  pardiso_iparm[63] = 0;
  void* pardiso_pt[64];
  for (int i = 0; i < 64; ++i) 
    pardiso_pt[i] = nullptr;
  

  auto error = MKL_INT0;
  auto phase = MKL_INT11;
  MKL_INT i_dummy;
  double d_dummy;
  PARDISO(pardiso_pt, &pardiso_max_fact, &pardiso_mnum, &pardiso_mtype,
          &phase, &m, values.data(), row.data(), column.data(), &i_dummy,
          &pardiso_nrhs, pardiso_iparm, &pardiso_msglvl, &d_dummy,
          &d_dummy, &error);
  phase = 22;
  PARDISO(pardiso_pt, &pardiso_max_fact, &pardiso_mnum, &pardiso_mtype,
          &phase, &m, values.data(), row.data(), column.data(), &i_dummy,
          &pardiso_nrhs, pardiso_iparm, &pardiso_msglvl, &d_dummy,
          &d_dummy, &error);
  phase = 33;
  for(size_t i = 0; i < 10000; ++i) 
    std::cout << "i = " << i << std::endl;
    PARDISO(pardiso_pt, &pardiso_max_fact, &pardiso_mnum, &pardiso_mtype,
            &phase, &m, values.data(), row.data(), column.data(), &i_dummy,
            &pardiso_nrhs, pardiso_iparm, &pardiso_msglvl, y.data(),
            x.data(), &error);
  
  phase = -1;
  PARDISO(pardiso_pt, &pardiso_max_fact, &pardiso_mnum, &pardiso_mtype,
          &phase, &m, values.data(), row.data(), column.data(), &i_dummy,
          &pardiso_nrhs, pardiso_iparm, &pardiso_msglvl, &d_dummy,
          &d_dummy, &error);

  return 0;

【讨论】:

以上是关于如何查找我是不是有内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 visualvm 查找内存泄漏

如何查找 Java 内存泄漏

如何自己检查NodeJS的代码是不是存在内存泄漏

如何在源代码中查找内存泄漏

如何在eclipse中使用MAT查找内存泄漏

如何从android中的本机代码中查找内存泄漏