近日,听说pthread_create会造成内存泄漏,觉得不可思议,因此对posix(nptl)的线程创建和销毁进行了分析。

 
分析结果:如果使用不当,确实会造成内存泄漏。
产生根源:pthread_create默认创建的线程是非detached的。
预防方式:要么创建detached的线程,要么线程线程的start_routine结束之前detached,要么join
 
分析过程如下:
 
  1.查看pthread_create源代码,核心代码如下(nptl/pthread_create.c):

点击(此处)折叠或打开

  1. int
  2. __pthread_create_2_1 (newthread, attr, start_routine, arg)
  3. pthread_t *newthread;
  4. const pthread_attr_t *attr;
  5. void *(*start_routine) (void *);
  6. void *arg;
  7. {
  8. STACK_VARIABLES;
  9. const struct pthread_attr *iattr = (struct pthread_attr *) attr;
  10. if (iattr == NULL)
  11. /* Is this the best idea? On NUMA machines this could mean
  12. accessing far-away memory. */
  13. iattr = &default_attr;
  14. struct pthread *pd = NULL;
  15. int err = ALLOCATE_STACK (iattr, &pd);//为tcb分配内存
  16. if (__builtin_expect (err != 0, 0))
  17. /* Something went wrong. Maybe a parameter of the attributes is
  18. invalid or we could not allocate memory. */
  19. return err;
  20. //……
  21. err = create_thread (pd, iattr, STACK_VARIABLES_ARGS);//正式创建线程
 

2.查看createthread.c(nptl/sysdeps/pthread/createthread.c)

点击(此处)折叠或打开

  1. static int
  2. create_thread (struct pthread *pd, const struct pthread_attr *attr,
  3. STACK_VARIABLES_PARMS)
  4. {
  5. #ifdef TLS_TCB_AT_TP
  6. assert (pd->header.tcb != NULL);
  7. #endif
  8. //……
  9. int res = do_clone (pd, attr, clone_flags, start_thread,
  10. STACK_VARIABLES_ARGS, 1);//clone一个进程

3.接着看start_thread(nptl/pthread_create.c)做了什么

点击(此处)折叠或打开

  1. static int
  2. start_thread (void *arg)
  3. {
  4. struct pthread *pd = (struct pthread *) arg;
  5. //……
  6. /* Run the code the user provided. */
  7. #ifdef CALL_THREAD_FCT
  8. THREAD_SETMEM (pd, result, CALL_THREAD_FCT (pd));
  9. #else
  10. THREAD_SETMEM (pd, result, pd->start_routine (pd->arg)); //正式启动线程的执行,并等待执行完成
  11. #endif
  12. //……
  13. if (IS_DETACHED (pd))
  14. /* Free the TCB.  */
  15. __free_tcb (pd);//如果设置detached标志,则释放tcb占用的内容,否则直接返回
  16. else if (__builtin_expect (pd->cancelhandling & SETXID_BITMASK, 0))
  17. {
  18. /* Some other thread might call any of the setXid functions and expect
  19. us to reply.  In this case wait until we did that.  */
  20. do
  21. lll_futex_wait (&pd->setxid_futex, 0, LLL_PRIVATE);
  22. while (pd->cancelhandling & SETXID_BITMASK);
  23. /* Reset the value so that the stack can be reused.  */
  24. pd->setxid_futex = 0;
  25. }

从上面的过程,我们可以看到,如果在创建线程的时候,如果没有设置detached标志,则tcb内存永远不会释放

 
接下来,我们看看pthread_detach(npth/pthread_detach.c)做了什么

点击(此处)折叠或打开

  1. int
  2. pthread_detach (th)
  3. pthread_t th;
  4. {
  5. struct pthread *pd = (struct pthread *) th;
  6. /* Make sure the descriptor is valid. */
  7. if (INVALID_NOT_TERMINATED_TD_P (pd))
  8. /* Not a valid thread handle. */
  9. return ESRCH;
  10. int result = 0;
  11. /* Mark the thread as detached. */
  12. if (atomic_compare_and_exchange_bool_acq (&pd->joinid, pd, NULL))
  13. {
  14. /* There are two possibilities here. First, the thread might
  15. already be detached. In this case we return EINVAL.
  16. Otherwise there might already be a waiter. The standard does
  17. not mention what happens in this case. */
  18. if (IS_DETACHED (pd))
  19. result = EINVAL;
  20. }
  21. else
  22. /* Check whether the thread terminated meanwhile. In this case we
  23. will just free the TCB. */
  24. if ((pd->cancelhandling & EXITING_BITMASK) != 0)
  25. /* Note that the code in __free_tcb makes sure each thread
  26. control block is freed only once. */
  27. __free_tcb (pd);//经过一系列的容错判断,直接释放tcb占用的内存
  28. return result;
  29. }

最后,我们看一下pthread_join(nptl/pthread_join.c)做了什么

点击(此处)折叠或打开

  1. int
  2. pthread_join (threadid, thread_return)
  3. pthread_t threadid;
  4. void **thread_return;
  5. {
  6. struct pthread *pd = (struct pthread *) threadid;
  7. /* Make sure the descriptor is valid. */
  8. if (INVALID_NOT_TERMINATED_TD_P (pd))
  9. /* Not a valid thread handle. */
  10. return ESRCH;
  11. /* Is the thread joinable?. */
  12. if (IS_DETACHED (pd))
  13. /* We cannot wait for the thread. */
  14. return EINVAL;
  15. struct pthread *self = THREAD_SELF;
  16. int result = 0;
  17. /* During the wait we change to asynchronous cancellation. If we
  18. are canceled the thread we are waiting for must be marked as
  19. un-wait-ed for again. */
  20. pthread_cleanup_push (cleanup, &pd->joinid);
  21. /* Switch to asynchronous cancellation. */
  22. int oldtype = CANCEL_ASYNC ();
  23. if ((pd == self
  24. || (self->joinid == pd
  25. && (pd->cancelhandling
  26. & (CANCELING_BITMASK | CANCELED_BITMASK | EXITING_BITMASK
  27. | TERMINATED_BITMASK)) == 0))
  28. && !CANCEL_ENABLED_AND_CANCELED (self->cancelhandling))
  29. /* This is a deadlock situation. The threads are waiting for each
  30. other to finish. Note that this is a "may" error. To be 100%
  31. sure we catch this error we would have to lock the data
  32. structures but it is not necessary. In the unlikely case that
  33. two threads are really caught in this situation they will
  34. deadlock. It is the programmer's problem to figure this
  35. out. */
  36. result = EDEADLK;
  37. /* Wait for the thread to finish. If it is already locked something
  38. is wrong. There can only be one waiter. */
  39. else if (__builtin_expect (atomic_compare_and_exchange_bool_acq (&pd->joinid,
  40. self,
  41. NULL), 0))
  42. /* There is already somebody waiting for the thread. */
  43. result = EINVAL;
  44. else
  45. /* Wait for the child. */
  46. lll_wait_tid (pd->tid);
  47. /* Restore cancellation mode. */
  48. CANCEL_RESET (oldtype);
  49. /* Remove the handler. */
  50. pthread_cleanup_pop (0);
  51. if (__builtin_expect (result == 0, 1))
  52. {
  53. /* We mark the thread as terminated and as joined. */
  54. pd->tid = -1;
  55. /* Store the return value if the caller is interested. */
  56. if (thread_return != NULL)
  57. *thread_return = pd->result;//设置返回值
  58. /* Free the TCB. */
  59. __free_tcb (pd);/释放TCB占用内存
  60. }
  61. return result;
  62. }
综上,如果要保证创建线程之后,确保无内存泄漏,必须采用如下方法来规范pthread_create的使用:
方法一、创建detached的线程

点击(此处)折叠或打开

  1. void run() {
  2. return;
  3. }
  4. int main(){
  5. pthread_t thread;
  6. pthread_attr_t attr;
  7. pthread_attr_init( &attr );
  8. pthread_attr_setdetachstate(&attr,1);
  9. pthread_create(&thread, &attr, run, 0);
  10. //......
  11. return 0;
  12. }
方法二、要么线程线程的start_routine结束之前detached

点击(此处)折叠或打开

  1. void run() {
  2. pthread_detach(pthread_self());
  3. }
  4. int main(){
  5. pthread_t thread;
  6. pthread_create(&thread, NULL, run, 0);
  7. //......
  8. return 0;
  9. }
方法三、主线程使用pthread_join

点击(此处)折叠或打开

  1. void run() {
  2. return;
  3. }
  4. int main(){
  5. pthread_t thread;
  6. pthread_create(&thread, NULL, run, 0);
  7. //......
  8. pthread_join(thread,NULL);
  9. return 0;
  10. }

正确使用pthread_create,防止内存泄漏的更多相关文章

  1. iOS 出现内存泄漏的几种原因

    一.从AFNet 对于iOS开发者,网络请求类AFNetWorking是再熟悉不过了,对于AFNetWorking的使用我们通常会对通用参数.网址环境切换.网络状态监测.请求错误信息等进行封装.在封装 ...

  2. Android内存泄漏原因

    这段时间调试APP的时候,发现程序在加载了过多的bitmap后会崩溃.查看了日志,原来是发生了内存溢出(OOM).第一次遇到这样的问题,那就慢慢排查吧. 内存优化可以参考胡凯大神的博客Android内 ...

  3. ios开发系列之内存泄漏分析(上)

    ios自从引入ARC机制后,一般的内存管理就可以不用我们码农来负责了,但是一些操作如果不注意,还是会引起内存泄漏. 本文主要介绍一下内存泄漏的原理.常规的检测方法以及出现的常用场景和修改方法. 1.  ...

  4. 【知识必备】内存泄漏全解析,从此拒绝ANR,让OOM远离你的身边,跟内存泄漏say byebye

    一.写在前面 对于C++来说,内存泄漏就是new出来的对象没有delete,俗称野指针:而对于java来说,就是new出来的Object放在Heap上无法被GC回收:而这里就把我之前的一篇内存泄漏的总 ...

  5. java内存泄漏的几种情况

    转载于http://blog.csdn.net/wtt945482445/article/details/52483944 Java 内存分配策略 Java 程序运行时的内存分配策略有三种,分别是静态 ...

  6. Android内存泄漏分享

    内容概述 内存泄漏和内存管理相关基础. Android中的内存使用. 内存分析工具和实践. 以下内容不考虑非引用类型的数据,或者将其等同为对应的引用类型看待--一切皆对象. 内存泄漏概念 不再使用的对 ...

  7. .net中事件引起的内存泄漏分析

    系列主题:基于消息的软件架构模型演变 在Winform和Asp.net时代,事件被大量的应用在UI和后台交互的代码中.看下面的代码: private void BindEvent() { var bt ...

  8. Android内存优化-内存泄漏的几个场景以及解决方式

    转自:http://blog.csdn.net/a910626/article/details/50849760 一.什么是内存泄漏 在Java程序中,如果一个对象没有利用价值了,正常情况下gc是会对 ...

  9. 【原创】android内存管理-内存泄漏原因

    转载请注明出处 http://www.cnblogs.com/weiwangnuanyang/p/5704596.html 先讲一下内存泄漏的概念:内存泄露是指无用对象持续占有内存,或者内存得不到及时 ...

随机推荐

  1. java byte中存大于0x7E的十六进制数

    在做一个Android app和arm板子交互的程序中,遇到一个问题,Java byte中无法直接存储大于0x7E的十六进制,但是C语言却可以. 出现这个状况的原因是:Java中是byte存储的是有符 ...

  2. [NOIP2013]车站分级 解题报告

    妈蛋这道普及组水(神)题搞了我非常久. 一. 首先一个非常显然的事情就是每一个火车告诉了站与站之间的等级关系,所以拓扑求最长路. 可是发现暴力建边的话最坏能够达到500*500,所以时间复杂度有O(M ...

  3. VPS 上线监控监控脚本

    文件地址 https://github.com/yourshell/yisuo-script/blob/master/vpstz/vpsmon.zip https://download.csdn.ne ...

  4. Android学习笔记进阶17之LinearGradient

    具体的看一下博文:Android学习笔记进阶15之Shader渲染 package xiaosi.BitmapShader; import android.app.Activity; import a ...

  5. js--递归详解

    1 函数的调用 eg1:阶乘算法 var f = function (x) { if (x === 1) { return 1; } else { return x * f(x - 1); } }; ...

  6. canvas.toDataURL() gives “Security Error” in IE 11

    http://stackoverflow.com/questions/30101143/canvas-todataurl-gives-security-error-in-ie-11

  7. Android怎样实现毛玻璃效果之Android高级模糊技术

    自从iOS系统引入了Blur效果,也就是所谓的毛玻璃.模糊化效果.磨砂效果.各大系统就開始竞相模仿,这是如何的一个效果呢,我们先来看一下,如以下的图片: 效果我们知道了,怎样在Android中实现呢. ...

  8. Linux常用运维命令小结

    1. 空设备文件以及标准输入输出 /dev/null 表示空设备文件 0 表示stdin标准输入 1 表示stdout标准输出 2 表示stderr标准错误 2>&1 这里有两种解释:将 ...

  9. python 的 reshape强制转换格式的用途

    shu=[[ 0.03046758], [ 0.05485586], [ 0.03282908], [ 0.02107211], [ 0.0391144 ], [ 0.07847244], [ 0.1 ...

  10. nuxt使用QRCode.js 生成二维码

    QRCode.js 是一个用于生成二维码图片的插件, 官方文档 . 我是在nuxt.js(vue官方的服务端渲染方式)项目里使用的QRCode.js: 第一步:看官方文档: 第二步:下载QRCode. ...