如何发现我是否有内存泄漏
How to find if I have a memory leak
我写了一个数字运算算法。这个想法是:
- 一个小的主程序需要很少的内存(启动时只有2mb) 然后,在循环中,它调用一个需要相当多内存(大约100 MB)的函数,这些内存应该在函数结束时释放。为了理解发生了什么,函数现在总是用相同的参数调用。
似乎程序慢慢地消耗内存,所以我怀疑内存泄漏。我试过Clang的地址消毒器和Intel的指针检查器,但他们没有找到任何东西。
现在,我正在查看我的活动监视器中的内存消耗(我正在运行OSX,但我从Unix命令"top"中获得相同的内存使用情况),就在调用大函数之前,程序需要2 MB。运行该函数时,程序需要120 MB。奇怪的是,当程序结束大函数并回到循环中时,它现在需要37 MB!然后,当它返回到大函数时,它需要130mb。再次回到循环中,它需要36mb,然后在大函数中它需要140mb…
所以它在慢慢地漂移,但不是有规律的。我该如何信任"top"中的内存使用情况?
内存碎片可以增加内存使用而不产生内存泄漏吗?
我让程序运行一夜,下面是我得到的数据:
- 在第一个循环中,程序占用150 MB
- 2小时后,68个循环后,程序占用220 MB
- 经过一个晚上和394个循环,程序占用480 MB
因此,似乎分配和释放内存的函数(大约120 MB)似乎每次被调用时都会"泄漏"1 MB。
首先,确保在很长一段时间内(例如,如果一个迭代需要一分钟,运行几个小时)增长是持续的。如果肿瘤逐渐缩小,那就没有问题了。接下来是valgrind
。然后,如果没有帮助,您将不得不对代码进行二进制搜索:注释掉比特,直到增长停止。我将首先完全删除MKL
库的使用(如果您愿意,请留下存根),然后看看会发生什么。接下来,将矢量更改为std::vector
,看看是否有帮助。在那之后,你就要用你的判断力了。
我想我已经找到了罪魁祸首:MKL(截至今天的最新版本)。我使用Pardiso,下面的例子泄漏非常慢:大约每13秒0.1 MB,一夜之间导致280mb。这些是我从模拟中得到的数字。
如果你想试一试,你可以这样编译:
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;
}
- 是否可以通过C++扩展强制多个python进程共享同一内存
- 我在二维向量中是否正确分配了内存
- 这个极客对极客的trie实现是否存在内存泄漏问题
- 添加静态constexpr成员是否会更改结构/类的内存映射
- 是否值得降低我的代码的可读性,以便在出现内存不足错误时提供异常安全性?
- 使用 ImageIO.read() 生成的图像是否仍然使用传递给它的相同内存?
- 内存围栏是否涉及内核
- 我是否生成线程并导致内存泄漏?
- 公共/私有/受保护是否会更改内存中结构的排列?
- 是否可以通过每次在内存中仅保存一个平铺来处理完整的平铺 tiff 图像?
- 在没有动态内存的世界中,我是否需要虚拟析构函数?
- std::p romise::set_value() 和 std::future::wait() 是否提供内存围栏?
- 具有相同特征的两个对象是否只在内存中存储一次?无论定义它们的函数是什么,都是不同的
- 是否可以使用分配器对象来释放另一个分配器分配的内存?
- 释放动态分配的内存时是否需要执行此额外步骤
- 当另一个数组是内存集时,内存集是否会更改数组长度?
- 内存中类位置的成员是否取决于类成员在类定义中的位置?
- 是否可以仅通过将分配的指针地址存储在C++中来分析内存?
- 当新的故障时,是否有必要留出一些紧急内存?
- 如何检查链表是否内存不足/已满