能防止大部分C程序内存泄漏的解决办法?


自 C 语言存在以来,内存泄漏就一直困扰着该语言。人们提出了许多解决方案,甚至建议我们用其他语言重写 C 程序。但还有更好的方法。

这里介绍的是一个简单的解决方案,它将消除每个 C 程序的内存泄漏。将其链接到您的程序中,内存泄漏将成为过去。

include <dlfcn.h>
include <stdio.h>

struct leaksaver {
        struct leaksaver *next;
        void *pointer;
} *bigbucket;

void *
malloc(size_t len)
{
        static void *(*nextmalloc)(size_t);
        nextmalloc = dlsym(RTLD_NEXT, "malloc");
        void *ptr = nextmalloc(len);
        if (ptr) {
                struct leaksaver *saver = nextmalloc(sizeof(*saver));
                saver->pointer = ptr;
                saver->next = bigbucket;
                bigbucket = saver;
        }
        return ptr;
}

每个分配的指针都会保存在大桶中,并保持可访问状态。即使程序中不存在对指针的其他引用,指针也不会泄漏。

现在,调用 free 完全是可选的。如果不调用 free,内存使用量会随着时间的推移而增加,但从技术上讲,这并不是泄漏。

作为一种优化,你可以选择调用 free 来减少内存,但同样是完全可选的。

网友评论:

  • 这种处理内存泄漏的方法并非无懈可击,因为它仍会保留所有分配。它只是保留了所有分配的列表,但并没有解决核心问题,即如果不调用 free,就会产生不必要的内存消耗。但更紧迫的问题是,这种实现方式并不是线程安全的。在多线程程序中并发访问 bigbucket 列表很容易导致竞赛条件
  • 除非程序长期运行,否则在 C 语言程序中调用 free 没有任何意义。当程序结束时,系统会为你释放内存。无Free C 语言编程是一种令人兴奋的体验,我向所有人推荐。在极少数情况下,如果你的程序需要长期运行,泄漏可能会成为一个真正的问题。在这种情况下,最好编写一个长期运行的 shell 脚本,调用一系列优雅的无自由空间 C 程序。
  • 除了最迂腐的意义外,这种解决方案根本无法防止内存泄漏,而且实际上还会产生泄漏和悬空指针。
    该函数只是用一个包装器间接地使用了 malloc,这样所有的内存都可以被 "bigbucket "结构遍历。内存仍然是泄漏的,因为任何未释放的数据都将继续消耗堆内存,而且代码仍然无法访问,除非应用程序对 "bigbucket "结构做了某些操作,但这是不安全的。
    由于没有相应的 free() 调用,所以即使应用程序释放了内存分配,放入 "bigbucket "结构的数据也不会被删除。顾名思义,这就是泄漏,真是讽刺。在一个进行大量分配的应用程序中,即使代码中没有内存泄漏,"bigbucket "结构也可能耗尽堆。
  • 已经有一些工具可以识别内存泄漏,比如 LeakSanitiser https://clang.llvm.org/docs/LeakSanitizer.html。请使用这些工具。作者知道此类工具,因为其想法是欺骗它们。
  • 避免内存泄漏的更好方法是采用良好的编程实践,例如仔细管理内存分配和释放,使用静态分析器和内存调试器等自动化工具,以及在适用时利用具有自动内存管理功能的编程语言(例如垃圾收集) Java 或 Python 等语言)。
  • 显然这是一个笑话,但真正的信息应该是:如果您无法管理自己的内存,那么您应该使用具有自动内存管理功能的语言实现。如果您的代码确实需要“接近金属”运行,那么您确实需要弄清楚如何管理内存。到 2024 年,不带自动内存管理的语言实现应该保留给绝对需要它们的应用程序。
  • 这显然是一个笑话,但同时它实际上是解决正确问题的可行方法。有时泄漏是可以容忍的,并且就程序员的时间而言,非常便宜!
  • 用 Java 构建的 HFT shop,只是简单地关闭了垃圾收集器。然后,当市场收盘时,他们将在第二天重新启动该流程。
  • 我有个朋友在一家大型 Market Maker做市商工作,他告诉我他们确实会关闭 GC,但他们会做的只是事先将所有内容分配到大数组中,并用增量器来模拟 "new "关键字。他们可能会在类似线程本地的环境中这样做,以避免处理锁或竞赛条件之类的问题。
  • 许多游戏引擎都这样做,通过将所有内容分配为堆栈sack来避免 malloc 查找的成本
  • 如果运行时可用的 RAM 量大于程序的总内存需求,则空垃圾收集器是有效的垃圾收集器。