近日,听说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. 团队作业-Beta冲刺(2)

    这个作业属于哪个课程 https://edu.cnblogs.com/campus/xnsy/SoftwareEngineeringClass2 这个作业要求在哪里 https://edu.cnblo ...

  2. Sql延时

    IF EXISTS(SELECT * FROM sys.procedures WHERE name='usp_wait30s')BEGIN DROP PROC usp_wait30sENDgocrea ...

  3. JSTL之C标签学习

    JSTL 核心标签库标签共有13个,功能上分为4类: 1.表达式控制标签:out.set.remove.catch 2.流程控制标签:if.choose.when.otherwise 3.循环标签:f ...

  4. 用djbdns为域名解析服务护航

      上期回顾:http://chenguang.blog.51cto.com/350944/292195       650) this.width=650;" alt="&quo ...

  5. Maven搭建hadoop环境报Missing artifact jdk.tools:jdk.tools:jar:1.7(5种办法,2种正解)

    刚刚写的那一篇,是网上比较主流的解决办法. 鉴于实际情况,有伙伴的机器上没有遇到这个问题,我们再探究原因,最终还有4种情况需要说明. 先说,另外一种"正解". <depend ...

  6. 记2018/4/29 qbxt 测试

    记 2018/4/29  qbxt 测试(提高基础班) 简单的 NOIP 模拟赛 竞赛时间: 2018 年 4 月 29 日 13:30-17:00 题目名称 乘法 求和 计数 输入文件名 mul.i ...

  7. javascript脚本从载入浏览器到显示执行的过程解析

    版权声明:本文为博主原创文章,未经博主允许不得转载. 简单的代码: <script type="text/javascript" src="xxx.js" ...

  8. fromCharCode vs chr

    fromCharCode vs chr echo off set "fn=%*" set php=d:/www/php5/php.exe cls echo. %php% %fn% ...

  9. selenium 自动化基础知识(各种定位)

    元素的定位 webdriver 提供了一很多对象定位方法  例如: [ id ] , name , class name , link text , partial link text , tag n ...

  10. 国内计算机类期刊 SCI收录:

    国内计算机类期刊 SCI收录: JOURNAL OF COMPUTER SCIENCE AND TECHNOLOGY,计算机科学与技术,英文,双月刊, SCIE 国内计算机类期刊 EI收录: 核心类 ...