打开和关闭日志文件会导致C中的内存泄漏



调试遗留代码。我有一个用C编写的程序。它实际上要长得多,但我创建了一个可复制的小程序来显示问题(请忽略这个程序没有多大意义的事实(。程序:

#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <malloc.h>
#include <ctype.h>
#include <glib.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <stdio.h>

#define FILE_SEP "/"
#define LENGTH 10
FILE *log_file;
char TMP[LENGTH];
char RESULT_DIRECTORY[LENGTH + 12];
int open_log() {
GString *path = g_string_new(RESULT_DIRECTORY);
g_string_append(path, FILE_SEP);
g_string_append(path, TMP);
g_string_append(path,".usr");
log_file = fopen(path->str,"w");
if (!log_file)
return 1;

return 0;
}
void close_log() {
fclose(log_file);
}

int main(int argc, char *argv[]) {
strcpy(TMP, "tmp");
strcpy(RESULT_DIRECTORY, "/home/");
if (open_log()) {
return 1;
}
close_log();
return 0;
}

运行valgrind我得到:

> valgrind --tool=memcheck --track-origins=yes --leak-check=full --show-reachable=yes program
==84011== Memcheck, a memory error detector
==84011== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==84011== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==84011== Command: c_status_reader
==84011== 
==84011== 
==84011== HEAP SUMMARY:
==84011==     in use at exit: 5,292 bytes in 9 blocks
==84011==   total heap usage: 10 allocs, 1 frees, 5,860 bytes allocated
==84011== 
==84011== 16 bytes in 1 blocks are possibly lost in loss record 1 of 7
==84011==    at 0x4C29104: malloc (vg_replace_malloc.c:299)
==84011==    by 0x4C29278: realloc (vg_replace_malloc.c:785)
==84011==    by 0x5078ABD: g_realloc (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x5092B13: ??? (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x5093C99: g_string_sized_new (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x5093D14: g_string_new (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x108BE0: open_log (program.c:21)
==84011==    by 0x108A6E: main (program.c:40)
==84011== 
==84011== 252 bytes in 1 blocks are still reachable in loss record 2 of 7
==84011==    at 0x4C27393: calloc (vg_replace_malloc.c:711)
==84011==    by 0x5078B39: g_malloc0 (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x508CCE2: ??? (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x508EB5C: g_slice_alloc (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x5093C6A: g_string_sized_new (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x5093D14: g_string_new (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x108BE0: open_log (program.c:21)
==84011==    by 0x108A6E: main (program.c:40)
==84011== 
==84011== 496 bytes in 1 blocks are possibly lost in loss record 3 of 7
==84011==    at 0x4C2710E: memalign (vg_replace_malloc.c:858)
==84011==    by 0x4C271A9: posix_memalign (vg_replace_malloc.c:1021)
==84011==    by 0x508D4B0: ??? (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x508ECE2: g_slice_alloc (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x5093C6A: g_string_sized_new (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x5093D14: g_string_new (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x108BE0: open_log (program.c:21)
==84011==    by 0x108A6E: main (program.c:40)
==84011== 
==84011== 504 bytes in 1 blocks are still reachable in loss record 4 of 7
==84011==    at 0x4C27393: calloc (vg_replace_malloc.c:711)
==84011==    by 0x5078B39: g_malloc0 (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x508CCC3: ??? (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x508EB5C: g_slice_alloc (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x5093C6A: g_string_sized_new (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x5093D14: g_string_new (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x108BE0: open_log (program.c:21)
==84011==    by 0x108A6E: main (program.c:40)
==84011== 
==84011== 504 bytes in 1 blocks are still reachable in loss record 5 of 7
==84011==    at 0x4C27393: calloc (vg_replace_malloc.c:711)
==84011==    by 0x5078B39: g_malloc0 (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x508CD2A: ??? (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x508EB5C: g_slice_alloc (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x5093C6A: g_string_sized_new (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x5093D14: g_string_new (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x108BE0: open_log (program.c:21)
==84011==    by 0x108A6E: main (program.c:40)
==84011== 
==84011== 1,488 bytes in 3 blocks are possibly lost in loss record 6 of 7
==84011==    at 0x4C2710E: memalign (vg_replace_malloc.c:858)
==84011==    by 0x4C271A9: posix_memalign (vg_replace_malloc.c:1021)
==84011==    by 0x508D4B0: ??? (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x508ED22: g_slice_alloc (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x5093C6A: g_string_sized_new (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x5093D14: g_string_new (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x108BE0: open_log (program.c:21)
==84011==    by 0x108A6E: main (program.c:40)
==84011== 
==84011== 2,032 bytes in 1 blocks are still reachable in loss record 7 of 7
==84011==    at 0x4C27393: calloc (vg_replace_malloc.c:711)
==84011==    by 0x5078B39: g_malloc0 (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x508EB12: g_slice_alloc (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x5093C6A: g_string_sized_new (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x5093D14: g_string_new (in /usr/lib64/libglib-2.0.so.0.2200.5)
==84011==    by 0x108BE0: open_log (program.c:21)
==84011==    by 0x108A6E: main (program.c:40)
==84011== 
==84011== LEAK SUMMARY:
==84011==    definitely lost: 0 bytes in 0 blocks
==84011==    indirectly lost: 0 bytes in 0 blocks
==84011==      possibly lost: 2,000 bytes in 5 blocks
==84011==    still reachable: 3,292 bytes in 4 blocks
==84011==         suppressed: 0 bytes in 0 blocks
==84011== 
==84011== For counts of detected and suppressed errors, rerun with: -v
==84011== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 6 from 5)

我想我理解这个问题——它在某个地方错过了g_string_free调用。但是,当我实际使用它时,它会说:still reachable: 5,276 bytes in 8 blocks。例如,我只有以下open_log(并且没有调用close_log(:

int open_log() {
GString *path = g_string_new(RESULT_DIRECTORY);
g_string_append(path, FILE_SEP);
g_string_free(path,TRUE);
//log_file = fopen(path->str,"w");
//if (!log_file)
//    return 1;
return 0;
}

我得到:

==88254==
==88254== LEAK SUMMARY:
==88254==    definitely lost: 0 bytes in 0 blocks
==88254==    indirectly lost: 0 bytes in 0 blocks
==88254==      possibly lost: 0 bytes in 0 blocks
==88254==    still reachable: 5,276 bytes in 8 blocks
==88254==         suppressed: 0 bytes in 0 blocks

但是,如果我删除g_string_append(path, FILE_SEP);,那么它实际上是有效的!所以我猜,当我执行g_string_free时,它只删除了初始字符串path,而没有删除附加部分。这是正确的吗?我该如何解决这些问题?

编辑

实际上,我尝试的第一个想法是在fopen之后添加g_string_free(path, TRUE);,但我仍然看到内存泄漏。代码:

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <stdio.h>
#include<string.h>
#include<malloc.h>
#include<ctype.h>
#include<glib.h>
#include<stdlib.h>

#define FILE_SEP "/"
#define LENGTH 10
FILE *log_file;
char TMP[LENGTH];
char RESULT_DIRECTORY[LENGTH + 12];
int open_log(void) {
GString *path = g_string_new(RESULT_DIRECTORY);
g_string_append(path, FILE_SEP);
g_string_append(path, TMP);
g_string_append(path, ".usr");
log_file = fopen(path->str, "w");
g_string_free(path, TRUE);

if (!log_file)
return 1;

return 0;
}
void close_log() {
if (log_file) {
fclose(log_file);
log_file = NULL;
}
}

int main(int argc, char *argv[]) {
strcpy(TMP, "tmp");
strcpy(RESULT_DIRECTORY, "/home/");
if (open_log()) {
return 1;
}
close_log();
return 0;
}

valgrind输出:

==23525== Memcheck, a memory error detector
==23525== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==23525== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==23525== Command: c_status_reader
==23525== 
==23525== 
==23525== HEAP SUMMARY:
==23525==     in use at exit: 5,276 bytes in 8 blocks
==23525==   total heap usage: 10 allocs, 2 frees, 5,860 bytes allocated
==23525== 
==23525== 252 bytes in 1 blocks are still reachable in loss record 1 of 6
==23525==    at 0x4C27393: calloc (vg_replace_malloc.c:711)
==23525==    by 0x5078B39: g_malloc0 (in /usr/lib64/libglib-2.0.so.0.2200.5)
==23525==    by 0x508CCE2: ??? (in /usr/lib64/libglib-2.0.so.0.2200.5)
==23525==    by 0x508EB5C: g_slice_alloc (in /usr/lib64/libglib-2.0.so.0.2200.5)
==23525==    by 0x5093C6A: g_string_sized_new (in /usr/lib64/libglib-2.0.so.0.2200.5)
==23525==    by 0x5093D14: g_string_new (in /usr/lib64/libglib-2.0.so.0.2200.5)
==23525==    by 0x108C31: open_log (program.c:21)
==23525==    by 0x108ABC: main (program.c:45)
==23525== 
==23525== 496 bytes in 1 blocks are still reachable in loss record 2 of 6
==23525==    at 0x4C2710E: memalign (vg_replace_malloc.c:858)
==23525==    by 0x4C271A9: posix_memalign (vg_replace_malloc.c:1021)
==23525==    by 0x508D4B0: ??? (in /usr/lib64/libglib-2.0.so.0.2200.5)
==23525==    by 0x508ECE2: g_slice_alloc (in /usr/lib64/libglib-2.0.so.0.2200.5)
==23525==    by 0x5093C6A: g_string_sized_new (in /usr/lib64/libglib-2.0.so.0.2200.5)
==23525==    by 0x5093D14: g_string_new (in /usr/lib64/libglib-2.0.so.0.2200.5)
==23525==    by 0x108C31: open_log (program.c:21)
==23525==    by 0x108ABC: main (program.c:45)
==23525== 
==23525== 504 bytes in 1 blocks are still reachable in loss record 3 of 6
==23525==    at 0x4C27393: calloc (vg_replace_malloc.c:711)
==23525==    by 0x5078B39: g_malloc0 (in /usr/lib64/libglib-2.0.so.0.2200.5)
==23525==    by 0x508CCC3: ??? (in /usr/lib64/libglib-2.0.so.0.2200.5)
==23525==    by 0x508EB5C: g_slice_alloc (in /usr/lib64/libglib-2.0.so.0.2200.5)
==23525==    by 0x5093C6A: g_string_sized_new (in /usr/lib64/libglib-2.0.so.0.2200.5)
==23525==    by 0x5093D14: g_string_new (in /usr/lib64/libglib-2.0.so.0.2200.5)
==23525==    by 0x108C31: open_log (program.c:21)
==23525==    by 0x108ABC: main (program.c:45)
==23525== 
==23525== 504 bytes in 1 blocks are still reachable in loss record 4 of 6
==23525==    at 0x4C27393: calloc (vg_replace_malloc.c:711)
==23525==    by 0x5078B39: g_malloc0 (in /usr/lib64/libglib-2.0.so.0.2200.5)
==23525==    by 0x508CD2A: ??? (in /usr/lib64/libglib-2.0.so.0.2200.5)
==23525==    by 0x508EB5C: g_slice_alloc (in /usr/lib64/libglib-2.0.so.0.2200.5)
==23525==    by 0x5093C6A: g_string_sized_new (in /usr/lib64/libglib-2.0.so.0.2200.5)
==23525==    by 0x5093D14: g_string_new (in /usr/lib64/libglib-2.0.so.0.2200.5)
==23525==    by 0x108C31: open_log (program.c:21)
==23525==    by 0x108ABC: main (program.c:45)
==23525== 
==23525== 1,488 bytes in 3 blocks are still reachable in loss record 5 of 6
==23525==    at 0x4C2710E: memalign (vg_replace_malloc.c:858)
==23525==    by 0x4C271A9: posix_memalign (vg_replace_malloc.c:1021)
==23525==    by 0x508D4B0: ??? (in /usr/lib64/libglib-2.0.so.0.2200.5)
==23525==    by 0x508ED22: g_slice_alloc (in /usr/lib64/libglib-2.0.so.0.2200.5)
==23525==    by 0x5093C6A: g_string_sized_new (in /usr/lib64/libglib-2.0.so.0.2200.5)
==23525==    by 0x5093D14: g_string_new (in /usr/lib64/libglib-2.0.so.0.2200.5)
==23525==    by 0x108C31: open_log (program.c:21)
==23525==    by 0x108ABC: main (program.c:45)
==23525== 
==23525== 2,032 bytes in 1 blocks are still reachable in loss record 6 of 6
==23525==    at 0x4C27393: calloc (vg_replace_malloc.c:711)
==23525==    by 0x5078B39: g_malloc0 (in /usr/lib64/libglib-2.0.so.0.2200.5)
==23525==    by 0x508EB12: g_slice_alloc (in /usr/lib64/libglib-2.0.so.0.2200.5)
==23525==    by 0x5093C6A: g_string_sized_new (in /usr/lib64/libglib-2.0.so.0.2200.5)
==23525==    by 0x5093D14: g_string_new (in /usr/lib64/libglib-2.0.so.0.2200.5)
==23525==    by 0x108C31: open_log (program.c:21)
==23525==    by 0x108ABC: main (program.c:45)
==23525== 
==23525== LEAK SUMMARY:
==23525==    definitely lost: 0 bytes in 0 blocks
==23525==    indirectly lost: 0 bytes in 0 blocks
==23525==      possibly lost: 0 bytes in 0 blocks
==23525==    still reachable: 5,276 bytes in 8 blocks
==23525==         suppressed: 0 bytes in 0 blocks
==23525== 
==23525== For counts of detected and suppressed errors, rerun with: -v
==23525== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 5)

为什么它没有解决问题?我甚至尝试将path设置为全局var,并调用close_log函数中的g_string_free。但它没有起作用。

您的第一个假设是正确的,您在open_logclose_log中都没有调用g_string_free,因此会发生内存泄漏。

对于

但是,当我实际使用它时,它会说:still reachable: 5,276 bytes in 8 blocks.。例如,我只有以下open_log(并且没有调用close_log(:

引用中的最后一句话就是答案。您没有调用close_log,因此FILE *log_file现在泄漏。

如果我删除g_string_append(path, FILE_SEP);,那么它实际上有效!

如果删除g_string_append,则log_file = fopen(path->str,"w");无法打开文件,因为它是一个目录,fopen返回NULL,并且没有任何泄漏。

如果你在所有修复后都不断出现内存泄漏,那么你就会看到valgrind关于油嘴滑舌的内部内容的误报报告。能说会道的开发人员非常友善,他们提供了一个特殊的文件来抑制valgrims中的假阳性报告:

/usr/share/glib-2.0/valgrind/glib.supp

此路径可能会发生变化,查找glib.supp,使用命令locate valgrind/glib.supp。使用带有valgrind的文件:

valgrind --suppressions=/usr/share/glib-2.0/valgrind/glib.supp <...>

pathopen_log()中分配了g_string_new,但在函数返回之前,它既没有释放,也没有存储在其他地方,因此导致内存泄漏。

在返回之前用g_string_free释放此字符串:

int open_log(void) {
GString *path = g_string_new(RESULT_DIRECTORY);
g_string_append(path, FILE_SEP);
g_string_append(path, TMP);
g_string_append(path, ".usr");
log_file = fopen(path->str, "w");
g_string_free(path, TRUE);
if (!log_file)
return 1;

return 0;
}

为了保持清洁,我建议在关闭后将log_file设置为NULL

void close_log(void) {
if (log_file) {
fclose(log_file);
log_file = NULL;
}
}

这应该可以解决区块不再可访问的问题,但Glib可能会为自己的记账或成功调用fopen分配一些区块,这些区块仍在等待Valgrind报告。

EDIT:修复内存泄漏后,程序退出时仍有8个块已分配且可访问:这些块均由g_string_new分配,可能用于GLib字符串包中的辅助数据。众所周知,该软件包在valgrind方面存在一些问题,可能有一些方法可以降低噪音,但您可以出于自己的目的忽略这些块。

不需要仅为此目的使用GLib。为了避免副作用,这里有一个替代方案:

int open_log(void) {
char path[1024];
snprintf(path, sizeof(path), "%s%s%s.usr",
RESULT_DIRECTORY, FILE_SEP, TMP);
log_file = fopen(path, "w");
if (!log_file)
return 1;

return 0;
}

最新更新