我在macOS Sierra 10.12.3,Xcode 8.2.1和Instruments中运行C代码的玩具示例,以显示内存泄漏和分配。
似乎仪器无法正常工作,或者编译器或运行时引擎足够智能,可以自行解决内存泄漏问题。所有这一切除非我在这里犯了一个错误,这是一个非常真实的第三选择。让我解释一下:
这是我的代码,第一版:
#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中的相同)。这次我只想相应地释放一半的分配和仪器报告(即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;
}
}
-
我将回到最初的问题:即使仪器没有报告,第3版是否真的存在内存泄漏?如果没有内存泄漏那么为什么不呢?
答案 0 :(得分:1)
似乎在版本3中没有内存泄漏(因此仪器运行良好)。
似乎编译器在得出我正在分配的内存永远不会被使用时进行优化。在这种情况下,它将生成甚至不会调用calloc
的代码。
证明我的陈述的理想方法是查看生成的代码或了解编译器是否充满信心。我不能这样做。相反,基于以下内容,我认为我说的是真的。
我已经分析了我的进程使用的虚拟内存的大小,它与Instruments报告的内容以及我对每个代码版本的期望一致。请参阅*下面我是如何做到的
我已经播放了不同版本的代码,正如我在2月27日的评论中所解释的那样,一旦我分配,免费或写入标准输出,一些已分配的内存工具就会报告{{1}操作。看来这条指令告诉编译器它无法避免调用calloc
。但是,当我不执行这些指令并且我不使用分配的内存或只读取内存中的值(即读取到从未使用过的变量)时,编译器正在优化而不是调用calloc
。
*这是我读取进程消耗的内存的方式:
我在乐器中运行该过程。我的进程永远不会完成,因为我有一个getchar来停止程序。仪器根据我的程序名称命名我的过程(这在工具栏中可见。在我的案例中,MemoryLeak)
我运行calloc
找到我的流程的pid(Leak确实引用了我的程序名称)
我运行ps -e | grep Leak
***编辑6月/ 3月17日:基于@n.m的评论。在3月5日发布到原始问题,我们可以看到(a)编译器生成的代码不会在代码的第3版中调用ps -p <pid> -ovsize
。