为啥 pthread 会导致内存泄漏

Posted

技术标签:

【中文标题】为啥 pthread 会导致内存泄漏【英文标题】:why pthread causes a memory leak为什么 pthread 会导致内存泄漏 【发布时间】:2013-07-12 14:53:36 【问题描述】:

每当我创建一个 pthread 时,valgrind 都会输出内存泄漏,

例如下面的代码:

#include <stdio.h>
#include <unistd.h>
#include <pthread.h> 

void *timer1_function (void *eit)
  (void) eit;
    printf("hello world\n");
    pthread_exit(NULL);


int main(void)
   pthread_t timer1;
   pthread_create( &timer1, NULL, timer1_function,  NULL);  ///////line13
   int i=0;
   for(i=0;i<2;i++)usleep(1);
   return 0;

valgrind 输出

==1395== HEAP SUMMARY:
==1395==     in use at exit: 136 bytes in 1 blocks
==1395==   total heap usage: 6 allocs, 5 frees, 1,134 bytes allocated
==1395== 
==1395== 136 bytes in 1 blocks are possibly lost in loss record 1 of 1
==1395==    at 0x402A629: calloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==1395==    by 0x4011304: allocate_dtv (dl-tls.c:297)
==1395==    by 0x4011AAB: _dl_allocate_tls (dl-tls.c:461)
==1395==    by 0x4052470: pthread_create@@GLIBC_2.1 (allocatestack.c:571)
==1395==    by 0x8048566: main (test.c:13)
==1395== 
==1395== LEAK SUMMARY:
==1395==    definitely lost: 0 bytes in 0 blocks
==1395==    indirectly lost: 0 bytes in 0 blocks
==1395==      possibly lost: 136 bytes in 1 blocks
==1395==    still reachable: 0 bytes in 0 blocks
==1395==         suppressed: 0 bytes in 0 blocks

虽然我使用手册页作为参考,但为什么 pthread_create 会导致问题,我该如何解决?

【问题讨论】:

valgrind memory leak errors when using pthread_create 的可能重复项 【参考方案1】:

我认为valgrind 会在程序退出时分析程序的状态,这可能在线程完成执行之前:两微秒可能不足以将"Hello, world!\n" 写入控制台。添加对pthread_join 的调用应该可以修复此泄漏:

pthread_join(timer1, NULL);

【讨论】:

您可以只为第二个参数传递一个空指针,而不是一个指向虚拟变量的指针。 @R.. 是的,我想你是对的...... OP 将NULL 传递给pthread_exit,所以pthread_join 不妨明确地忽略结果。谢谢!【参考方案2】:

线程是分配的资源,您在退出之前没有释放它。你应该打电话给pthread_join;这也将消除您对骇人听闻和不正确的睡眠循环的需要。

有可能即使你修复了这个问题,valgrind 仍然会看到“泄漏”,因为 POSIX 线程的一些实现(我猜你正在使用 glibc/NPTL)缓存并重用线程资源,而不是完全释放它们.我不确定 valgrind 是否可以解决这个问题。

【讨论】:

+1。我认为您的第二段解释了为什么我在我的答案中经常使用抑制方法。 pthread_join 的替代方案:使用 pthread_detach 但内存泄漏仍然存在。【参考方案3】:

内存泄漏是由于如果线程在没有取消的情况下继续运行,则相应的动态分配的内存不会被释放。使用 pthread_cancel() 和 pthread_cleanup_push(CleanupHandler, NULL) 和 pthread_cleanup_pop(0) 进行取消后的线程清理。

【讨论】:

【参考方案4】:

我在调用 pthread_join 失败时看到了类似的结果。

当我调用 pthread_join 时,Valgrind 将指示没有内存错误或泄漏。我使用 pthread_kill 得到了一个干净的结果来查看线程是否仍然存在,然后调用 join 来清理和释放资源。

int
stop_worker(worker_t *self)

    if (self) 
        // signal the thread to quit
            // (here using a variable and semaphore)
        self->thread_quit=TRUE;
        sem_post(&self->sem);

        // wait for it to stop
        // (could use counter, etc. to limit wait)
        int test=0;
        while (pthread_kill(self->thread,0) == 0) 
            MDEBUG(MD_XF_LOGGER,"waiting for thread to exit...\n",test);
            delay_msec(50);
        

        // even though thread is finished, need to call join
        // otherwise, it will not release its memory (and valgrind indicates a leak)
        test=pthread_join(self->thread,NULL);
        return 0;           
    
    return -1;

【讨论】:

【参考方案5】:

出现的泄漏与在子线程的本地存储 (tls) 中分配的 DTV(动态线程向量)结构有关。

在主线程(即产生子线程的线程)中使用pthread_join() 将确保修复泄漏。 对于不需要调用 pthread_join() 的用例,使用子 pthread_t 调用 pthread_detach 可确保释放内存。

来自pthread_detach的人:

pthread_detach()函数标记了thread标识的线程 作为分离。当一个分离的线程终止时,它的资源是 自动释放回系统,无需 另一个线程加入终止线程。

【讨论】:

以上是关于为啥 pthread 会导致内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

为啥使用“新”会导致内存泄漏?

为啥这会导致内存泄漏?

为啥这个 INotifyCollectionChanged 会导致内存泄漏?

为啥并行读取数组会导致内存泄漏?

为啥“[[UIDevice currentDevice] identifierForVendor]”会导致内存泄漏?

为啥使用 Activity 上下文会导致 Context 内存泄漏