pthread_create() 和内存泄漏
Posted
技术标签:
【中文标题】pthread_create() 和内存泄漏【英文标题】:pthread_create() and memory leaks 【发布时间】:2014-09-27 14:44:20 【问题描述】:这个问题似乎被问了很多。我有一些看起来不错的遗留生产代码,直到它开始每天获得更多连接。每个连接都启动了一个新线程。最终,它会耗尽内存并崩溃。
我将回顾多年未处理过的 pthread(和 C 套接字)。我的教程内容丰富,但是当我使用 top 时我看到了同样的事情。所有线程都退出了,但仍然占用了一些虚拟内存。 Valgrind 告诉我调用 pthread_create() 时可能会丢失内存。非常基本的示例代码如下。
最可怕的部分是 pthread_exit( NULL ) 似乎在所有线程退出时在 VIRT 中留下了大约 100m 的位置。如果我把这行注释掉,它会更宜居,但还是有一些。在我的系统上,它以大约 14k 开始,以 47k 结束。
如果我将线程数增加到 10,000,VIRT 会上升到 70+ gigs,但会在 50k 左右完成,假设我注释掉 pthread_exit(NULL)。如果我使用 pthread_exit( NULL ),它在 VIRT 中仍然有大约 113m。这些可以接受吗?上面没有告诉我一切吗?
void* run_thread( void* id )
int thread_id = *(int*)id;
int count = 0;
while ( count < 10 )
sleep( 1 );
printf( "Thread %d at count %d\n", thread_id, count++ );
pthread_exit( NULL );
return 0;
int main( int argc, char* argv[] )
sleep( 5 );
int thread_count = 0;
while( thread_count < 10 )
pthread_t my_thread;
if ( pthread_create( &my_thread, NULL, run_thread, (void*)&thread_count ) < 0 )
perror( "Error making thread...\n" );
return 1;
pthread_detach( my_thread );
thread_count++;
sleep( 1 );
pthread_exit( 0 ); // added as per request
return 0;
【问题讨论】:
我假设您正在报告您的数字的 valgrind 结果。你可以在return 0
之前在main()
中添加pthread_exit(0)
吗?
添加这似乎有助于...无论如何使用 valgrind。 'top' 似乎仍然在说有一些虚拟内存仍在使用中。该程序在启动线程之前有 14k 的 VIRT,现在有 110m。不过,这似乎是静态的。无论线程数是10还是1000,VIRT中的100m都在那里。我想我应该相信valgrind?
是的。 valgrind 报告内存问题的原因是在主线程结束之前并非所有线程都已完成。添加thread_exit()
调用解决了这个问题。
与内存泄漏无关,但您将同一本地 thread_count
变量的地址传递给每个新线程。根据线程启动的速度,它可能会看到正确的值,也可能会看到更新、更高的值。您需要在堆上分配一个新变量以传递给每个子线程,或者使用整数强制转换为(void*)
。
【参考方案1】:
我知道这是一个相当老的问题,但我希望其他人能从中受益。 这确实是内存泄漏。线程是使用默认属性创建的。默认情况下,线程是可连接的。可连接线程保持其底层簿记,直到它完成......并加入。 如果线程从未加入,则设置 de Detached 属性。一旦线程终止,所有(线程)资源将被释放。 这是一个例子:
pthread_attr_t attr;
pthread_t thread;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, 1);
pthread_create(&thread, &attr, &threadfunction, NULL);
pthread_attr_destroy(&attr);
【讨论】:
非常好——感谢您为后代回答一个老问题。当然,我现在只使用了 std::thread ,它似乎更好地封装了这一切。 :)【参考方案2】:在编辑将pthread_exit(0)
添加到main()
的末尾之前,您的程序将在所有线程完成运行之前完成执行。 valgrind
因此报告了在程序终止时仍处于活动状态的线程仍持有的资源,这使您的程序看起来像内存泄漏。
在main()
中对pthread_exit(0)
的调用使主线程在其自身退出之前等待所有其他派生线程退出。这让valgrind
可以观察到内存利用率方面的干净运行。
(我假设 linux 是您下面的操作系统,但您的 cmets 似乎正在运行各种 UNIX。)
您看到的额外虚拟内存只是 linux 为您的程序分配了一些页面,因为它是一个大内存用户。只要您在达到空闲状态时常驻内存利用率较低且恒定,并且虚拟利用率相对恒定,您就可以假设您的系统运行良好。
默认情况下,每个线程在 linux 上获得 2MB 的堆栈空间。如果每个线程堆栈不需要那么多空间,您可以通过初始化pthread_attr_t
并使用pthread_attr_setstacksize()
将其设置为较小的堆栈大小来调整它。合适的堆栈大小取决于您的函数调用堆栈增长的深度以及这些函数的局部变量占用多少空间。
#define SMALLEST_STACKSZ PTHREAD_STACK_MIN
#define SMALL_STACK (24*1024)
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, SMALL_STACK);
/* ... */
pthread_create(&my_thread, &attr, run_thread, (void *)thread_count);
/* ... */
pthread_attr_destroy(&attr);
【讨论】:
非常感谢 - 这很有意义!以上是关于pthread_create() 和内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章
使用 pthread_create 时出现 valgrind 内存泄漏错误