如何判断是否存在内存泄漏问题

7
我编写了一个数字计算算法。其思想是:
  1. 一个小的主程序需要很少的内存(起始为2 MB)
  2. 然后,在一个循环中,它调用一个需要相当多内存(约100 MB)的函数,该函数在结束时应该被释放。为了理解发生了什么,现在总是使用相同的参数调用该函数。
程序似乎会慢慢地占用内存,所以我怀疑存在内存泄漏。我已经尝试过Clang的Address Sanitizer和Intel的Pointer Checker,但它们都没有发现问题。
现在,我正在查看我的Activity Monitor中的内存消耗情况(我正在运行OSX,但从Unix命令“top”获取相同的内存使用情况),并且在调用大函数之前,程序占用2 MB。运行该函数时,程序占用120 MB。奇怪的是,当程序结束大函数并回到循环内部时,它现在占用37 MB!然后,当它重新进入大函数时,它占用130 MB。再次回到循环中,它占用36 MB,然后在大函数中占用140 MB......
因此,它慢慢地偏离,但没有固定的模式。我应该如何信任“top”中的内存使用情况?
内存碎片是否会增加内存使用量,但不会导致内存泄漏?
我让程序运行了一整夜,这是我得到的数据:
  1. 在第一个循环中,程序占用150 MB
  2. 2小时后,在68个循环之后,程序占用220 MB
  3. 经过一夜和394个循环后,程序占用480 MB
因此,似乎分配和释放内存(约120 MB)的函数似乎每次调用时都会“泄漏”1 MB。

6
使用 RAII 怎么样? - Sebastian Hoffmann
10
你尝试过valgrind吗? - NathanOliver
4
“内存碎片化是否会增加内存使用量而没有内存泄漏?” 是的。更确切地说,释放内存并不意味着您的操作系统必须立即从运行进程中清除它。 - Drew Dormann
3
因为各种原因。其中一个原因是我使用无符号整数作为索引,std::vector也是如此。我有一个调试模式,将所有的 il::Vector<double> 初始化为 NaN。通过 包装 std::vector 来实现这一点,而不是完全替换它。 - Lightness Races in Orbit
3
@tobi303它可靠地测量了某些内容,但那是操作系统保留的内存,而不是程序分配的内存。 - Drew Dormann
显示剩余14条评论
2个回答

6

首先,确保在长时间内(例如如果一个迭代需要一分钟,运行几个小时)增长仍在继续。如果增长渐近,则没有问题。接下来,我会尝试使用valgrind。然后,如果那也没有帮助,你将不得不对代码进行二分搜索:注释掉一些代码,直到增长停止。我会从完全删除MKL库的使用开始(如果想要留下存根,请留下),看看会发生什么。接下来,将向量更改为std::vector,只是为了看看是否有所帮助。之后,你将不得不根据自己的判断进行调整。


非常好的建议。我已经移除了MKL,看起来内存趋于渐近。仍在努力... - InsideLoop
问题可能已经解决。在发布问题之前,我已经解决了一个std::unordered_map中“我正在越界写入”的错误。修复了这个问题之后,我以为问题仍然存在,因为内存在最初的迭代中仍然慢慢增长。正如您所建议的那样,似乎内存碎片不会使使用的内存恒定,而是使其收敛到渐近线。我长时间运行它,现在就是这种情况。我会让它过夜看看会发生什么... - InsideLoop

1
我认为我已经找到了罪魁祸首: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_t{1000};
  auto m = MKL_INT{n * 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_INT{1};
  auto pardiso_max_fact = MKL_INT{1};
  auto pardiso_mnum = MKL_INT{1};
  auto pardiso_mtype = MKL_INT{11};
  auto pardiso_msglvl = MKL_INT{0};
  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_INT{0};
  auto phase = MKL_INT{11};
  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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接