近日,听说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. WinRAR 5.40无弹窗广告注册版下载

    WinRAR 5.40无弹窗广告注册版下载  资料来源  http://www.heminjie.com/network/6366.html WinRAR 5.40 下载安装后,打开压缩包文件会弹出广 ...

  2. Android 基于ijkplayer+Rxjava+Rxandroid+Retrofit2.0+MVP+Material Design的android万能播放器aaa

    MDPlayer万能播放器 MDPlayer,基于ijkplayer+Rxjava+Rxandroid+Retrofit2.0+MVP+Material Design的android万能播放器,可以播 ...

  3. 洛谷 P1068 分数线划定

    P1068 分数线划定 题目描述 世博会志愿者的选拔工作正在 A 市如火如荼的进行.为了选拔最合适的人才,A 市对 所有报名的选手进行了笔试,笔试分数达到面试分数线的选手方可进入面试.面试分数线根 据 ...

  4. 如何使用echo.js实现图片的懒加载(整理)

    如何使用echo.js实现图片的懒加载(整理) 一.总结 一句话总结:a.在img标签中添加data-echo属性加载真实图片:<img class="loading" sr ...

  5. C Tricks(十八)—— 整数绝对值的实现

    为正还是为负:(对 int 类型而言,第一位为符号位,其余为数值,则右移 31 位,再与 1 求与) 如果为正 ⇒ 返回原值 如果为负 ⇒ 对其二进制形式各位取反 + 1 int abs(int x) ...

  6. 4.auto详解

    #include <iostream> using namespace std; template <calss T1,class T2> auto add(T1 t1, T2 ...

  7. 【Codeforces Round #432 (Div. 2) B】Arpa and an exam about geometry

    [链接]h在这里写链接 [题意] 给你3个点A,B,C 问你能不能将纸绕着坐标轴上的一点旋转.使得A与B重合,B与C重合 [题解] 这3个点必须共圆. 则A,B,C不能为一条直线.否则无解. 共圆之后 ...

  8. 洛谷 P2118 比例简化

    P2118 比例简化 题目描述 在社交媒体上,经常会看到针对某一个观点同意与否的民意调查以及结果.例如,对某一观点表示支持的有1498 人,反对的有 902人,那么赞同与反对的比例可以简单的记为149 ...

  9. C# mongodb帮助类

    这是在C#连接MongoDB的帮助类,所使用的驱动是在Vs2015的Nuget管理器中下载的mongodb驱动. 下载第一个,会自动下载下面的两个,不要删除. 在配置文件中配置连接字符串connStr ...

  10. (转)Oracle命令

    转自:http://www.cnblogs.com/NaughtyBoy/p/3181052.html Oracle登录命令 1.运行SQLPLUS工具 C:\Users\wd-pc>sqlpl ...