【问题标题】:Perhaps a resource not released? What could cause this?也许资源没有释放?什么可能导致这种情况?
【发布时间】:2011-01-09 09:41:47
【问题描述】:

我正在做一个项目,结果出现了以下堆栈跟踪:

#0  0x0017c30c in _IO_flush_all_lockp () from /lib/libc.so.6
#1  0x0017d030 in _IO_cleanup () from /lib/libc.so.6
#2  0x0013e042 in exit () from /lib/libc.so.6
#3  0x00126bbe in __libc_start_main () from /lib/libc.so.6
#4  0x08049d11 in _start ()

(删除代码是因为内存泄漏已解决。当然还有其他的。不过,在将它们发布到此处之前,我会更加努力地追踪它们。:) 最初的问题可能与内存泄漏无关。 )

首先,我是否从初始堆栈跟踪中寻找正确的方向?在处理内存问题时,我以前从未见过这个。有什么想法吗?

编辑:有人说这是由于visual_mem_new0。该函数只是分配内存。它对插件->作者一无所知。

编辑:呃。在 strdup 填充内存之前的 memcopy。

编辑:好的,这消除了一个内存泄漏。我不相信最初的堆栈跟踪是关于内存泄漏的——例如它仍然存在。它试图释放一些我相信的资源。该程序的一部分使用了大量已编译的程序集(JIT 编译器),它在缓冲区的文件描述符之上使用 mmap'd 内存。我正在关闭文件。我需要对内存映射做些什么吗?

不过,我会继续努力清除这些内存泄漏。我最近做了一些与特定插件相关的事情。该程序仅在我运行该插件时才挂起,该插件使用我所说的内存映射。我不确定它可能是什么。我做了一些小的改动。最初我怀疑一个共享指针,我跟踪它的引用。它使用整个 libvisual 中使用的相同系统,并且没有出现特定的内存泄漏。无论如何,我希望有人对此有所了解。我想不出还有什么要补充的。

编辑:好的,在修订历史的帮助下对其进行了跟踪。以下代码有什么问题?我不能像那样将输出复制到自身上吗?

static inline int dump_stack(AvsCompilerContext *ctx)
{
    AvsCompilerArgument *pa;
    char output[2048];

    snprintf(output, 2047, "\ncompiler: stackdump: Stack dump\n");
    for (pa=(AvsCompilerArgument *)ctx->stack->base; pa < (AvsCompilerArgument *)ctx->stack->pointer; pa++) {
        snprintf(stderr, 2047, "%scompiler: stackdump: [%2d] = ", output, (pa - (AvsCompilerArgument *)ctx->stack->base));
        switch (pa->type) {
            case AvsCompilerArgumentInvalid:
                snprintf(output, 2047, "%sinvalid", output);
                break;

            case AvsCompilerArgumentConstant:
                snprintf(output, 2047, "%s%.2f", output, pa->value.constant);
                break;

            case AvsCompilerArgumentIdentifier:
                snprintf(output, 2047, "%s", pa->value.identifier);
                break;

            case AvsCompilerArgumentMarker: {
                char *markers[] = { "invalid", "function", "argument", NULL };
                snprintf(output, 2047, "%s--- %s marker ---", output, markers[pa->value.marker]);
                break;
            }

            case AvsCompilerArgumentPrivate:
                snprintf(output, 2047, "%sprivate", output);
                break;

        }
        snprintf(output, 2047, "\n");
    }

    avs_debug(print(output));
    return VISUAL_OK;
}

宏 avs_debug 什么都不做。我把它的内容注释掉了。

【问题讨论】:

  • 你知道,这里有原始代码可能会很好。 StackOverflow 的部分功能是回答后人的问题。
  • 该代码对任何人都没用。问题在于堆栈跟踪,它现在与该代码完全无关。该代码不再适用于该问题。
  • 我认为你应该开始另一个问题。
  • 当前问题不可读。我希望您不要期望没有关注此问题更改的任何人提供任何帮助。
  • 有什么要知道的?有内存泄漏,解决了。我没有删除任何文字。我刚刚删除了 sn-ps 代码,如果你真的被它所困扰,你可以随时在修订历史中查看。

标签: c linux debugging memory-leaks


【解决方案1】:

visual_plugin_info_new 调用分配内存的visual_mem_new0,您需要先释放插槽,然后再将它们分配到visual_plugin_info_copy。

【讨论】:

  • 具体来说,如 valgrind 输出所示,在使用 strdup 分配给插槽之前,您需要检查是否已经使用分配的内存初始化了插槽。基本上我会在每次使用 strdup 之前添加一个调试 if / printf 以确保目标位置不是需要在被破坏之前释放的有效指针。
  • 不是这样,贾斯汀。我在问题中澄清了。 @贾斯汀:那是不可能的。内存是新分配的。 dup_info = visual_plugin_info_new (); 创建一个新分配的 VisPluginInfo,它的 memset 为 NULL。该内存空间中尚未保存任何内容。
  • 我说的是 visual_plugin_info_new() 的作用。它调用了 visual_mem_new0 宏和 visual_object_initialize,但你是对的,它们都没有为各个插槽分配内存。虽然我很接近:)
  • 是的,你激发了我们的认识 :) 这一切都很有帮助。如果您想再看一下,我已经更新了一些问题。即使内存泄漏已经解决,问题仍然没有解决。我不相信这与内存泄漏有关。
【解决方案2】:

既然你在做strdup(),你应该使用free()释放这些值。我不确定visual_mem_free() 是否调用free()。如果您尝试free() 而不是visual_mem_free(),valgrind 错误会消失吗?

编辑:您的snprintf() 调用错误:

snprintf(output, 2047, "%sinvalid", output);

snprintf() 是 C99,标准说 (7.19.6.5p2):

如果复制发生在重叠的对象之间,则行为未定义。

C89 中的 sprintf() 也是如此。

解决问题的最简单方法是:

char init[] = "\ncompiler: stackdump: Stack dump\n";
size_t init_len = sizeof init - 1;
snprintf(output, 2047, "\ncompiler: stackdump: Stack dump\n");

接着是:

snprintf(output+init_len, sizeof output - init_len, "%.2f", pa->value.constant);

(请检查上面的错误。)

另外,我不确定您为什么在其中一个调用中使用stderr 作为其第一个参数来调用snprintf()。您是否在编译代码时启用了警告?

【讨论】:

  • 是的,它免费通话。我发现出了什么问题。 strdup 上方的 memcopy 分配了内存。不需要 strdup。
  • 太棒了。对噪音感到抱歉。
【解决方案3】:

在您发布的最后一个 sn-p 代码中可能至少存在 2 个问题(问题的编辑让您现在尝试处理的问题非常混乱):

  1. 您有一行以snprintf(stderr, 2047, etc...) 开头。这几乎可以肯定是一个复制粘贴错误,因为它不应该编译。您可能打算使用fprintf()

  2. 您不能将目标缓冲区也用作snprintf() 调用中的源。该标准说,“如果复制发生在重叠的对象之间,则行为是未定义的”。如果您考虑一下,您可能会意识到如果不先将缓冲区复制到其他地方,它在一般情况下是行不通的。尝试围绕vsnprintf()(可能命名为snprintfcat())创建一个包装器,将格式化的字符串连接到目标缓冲区。这并不难,虽然它需要使用可变参数,这可能有点棘手 - 但只有一点。

以下是对 snprintfcat() 函数的完全未经测试的镜头 - 它可以编译,但超出此范围使用需您自担风险:

int snprintfcat( char* dest, size_t siz, char const* fmt, ...)
{
    size_t len = strnlen( dest, siz);
    size_t remainder = 0;
    int result;

    va_list ap;

    if (len < siz) {
        remainder = siz - len;
    }

    va_start( ap, fmt);
    result = vsnprintf( dest+siz-remainder, remainder, fmt, ap);
    va_end( ap);

    return result + siz - remainder;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-20
    • 1970-01-01
    • 2021-08-15
    • 1970-01-01
    相关资源
    最近更新 更多