我正在 macOS Sierra 10.12.3、Xcode 8.2.1 和 Instruments 中运行 C 代码的玩具示例,以可视化内存泄漏和分配。
似乎是 Instruments 无法正常工作,或者编译器或运行时引擎足够聪明,可以自行解决内存泄漏问题。所有这一切,除非我在这里犯了一个错误,这是一个非常真实的第三种选择。让我解释一下:
这是我的代码,第一个版本:
#include <stdio.h>
#include <stdlib.h>
#define NUM_ARRAY_ELEMENS 10
#define NUM_POSITIONS_BY_ELEMEN 100
#define TIMES 200
int main(int argc, const char * argv[]) {
// insert code here...
printf("Hello, World!\n");
int *a[NUM_ARRAY_ELEMENS];
int cnt = 0;
while (cnt < TIMES) {
cnt++;
for (int i=0;i < NUM_ARRAY_ELEMENS ;i++) {
a[i] = (int*)calloc(NUM_POSITIONS_BY_ELEMEN,sizeof(int));
}
int *p;
for (int i=0;i < NUM_ARRAY_ELEMENS ;i++) {
//To free or access memory only on half of the positions
//if (i % 2 == 0) {
//To free memory
free(a[i]);
//Just using the memory allocated
//p = a[i];
//*p = 1;
//}
}
}
int c = getchar();
printf("Bye, World!\n");
return 0;
}
See screenshot of profile generated by Instruments for the version 1 of the code 您可以看到 Instruments 报告了 2000 个 MALLOC 操作以分配 400 字节并且没有内存泄漏。一切顺利
--
对于版本 2 一切仍然很好(未显示的代码部分与版本 1 中的相同)。这次我只想相应地释放一半的分配和 Instruments 报告(即 1000 个 MALLOC 是临时的,1000 个是持久的)并报告泄漏
int *p;
for (int i=0;i < NUM_ARRAY_ELEMENS ;i++) {
//To free or access memory only on half of the positions
if (i % 2 == 0) {
//To free memory
free(a[i]);
//Just using the memory allocated
//p = a[i];
//*p = 1;
}
}
我没有足够的信誉来粘贴超过 2 个链接,所以我会保留我的信誉以可视化有错误的实际版本
--
在版本 3 中出现问题(未显示的代码部分与版本 1 相同)。
int *p;
for (int i=0;i < NUM_ARRAY_ELEMENS ;i++) {
//To free or access memory only on half of the positions
if (i % 2 == 0) {
//To free memory
//free(a[i]);
//Just using the memory allocated
//p = a[i];
//*p = 1;
}
}
这次我预计所有 MALLOC 都会发生泄漏,并会看到 2000 个持久分配。但是没有报告任何问题。甚至没有显示 2000 次分配(既不是临时的也不是永久的;没有报告这些分配)
See screenshot of profile generated by Instruments for the version 3 of the code
--
那么这是怎么回事呢?编译器或运行时是否知道分配的内存未被使用并决定不分配它?
所以在第 4 版中,我访问分配的内存以查看这是否“停止”了“优化”并且确实如此。仪器将像在版本 2 中一样正确地报告分配和泄漏。
int *p;
for (int i=0;i < NUM_ARRAY_ELEMENS ;i++) {
//To free or access memory only on half of the positions
if (i % 2 == 0) {
//To free memory
//free(a[i]);
//Just using the memory allocated
p = a[i];
*p = 1;
}
}
--
我回到最初的问题:即使 Instruments 没有报告,版本 3 中是否真的存在内存泄漏?如果没有内存泄漏,那为什么不呢?
最佳答案
似乎在版本 3 中没有内存泄漏(因此 Instruments 运行良好)。
当编译器断定我正在分配的内存永远不会被使用时,它似乎正在进行优化。在这种情况下,它将生成甚至不调用 calloc
的代码。 .
证明我的陈述的理想方法是查看生成的代码或充分了解编译器以确信情况确实如此。这两者我都做不到。相反,我认为我所说的是基于以下内容。
我已经分析了我的进程使用的虚拟内存的大小,它与 Instruments 报告的内容以及我对每个代码版本的预期一致。请参阅*下面我是如何做到的
正如我在 2 月 27 日的评论中所解释的那样,我播放了不同版本的代码,一旦我分配、释放或写入标准输出,一些分配的内存仪器就会报告
calloc
操作。似乎这条指令告诉编译器它无法避免调用calloc
.但是当我不执行这些指令并且我不使用分配的内存或只是读取内存中的值(即读入一个从未使用过的变量)时,编译器正在优化而不是调用calloc
.
*这是我读取进程消耗的内存的方式:
我在 Instruments 中运行该过程。我的过程永远不会完成,因为我有一个 getchar 来停止程序。 Instruments 根据我的程序名称命名我的进程(这在工具栏中可见。在我的例子中是 MemoryLeak)
我运行
ps -e | grep Leak
找到我的进程的 pid(泄漏确实引用了我的程序的名称)我运行
ps -p <pid> -ovsize
*** 2017 年 3 月 6 日编辑:基于@n.m 的评论。 3 月 5 日发布到原始问题我们可以看到 (a) 编译器生成的代码不调用 calloc
在代码的版本 3 中。
关于c - C 编译器是否修复了一些明显的内存泄漏?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42460315/