近日,听说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. Kali Linux下安装VMware Tools

    引言 Kali Linux是基于Debian的Linux发行版, 设计用于数字取证和渗透測试.安装Kali Linux非常easy,可是安装VMware Tools的过程就有点麻烦了,由于在安装中途会 ...

  2. TextView-显示自己添加的字体样式

    1.首先要把我们的字体放到相应的目录下 如果我们仅仅是想要验证一个字体,我们可以直接 我们的字体push到 手机 /system/fonts/ 目录下面 2.在代码中进行设置 import andro ...

  3. setInterval 传参数

    <script type="text/javascript" > window.onload=function(){ for(var i=1;i<3;i++){ ...

  4. springMVC通过ajax传递参数list对象或传递数组对象到后台

    springMVC通过ajax传递参数list对象或传递数组对象到后台 环境: 前台传递参数到后台 前台使用ajax 后台使用springMVC 传递的参数是N多个对象 JSON对象和JSON字符串 ...

  5. git 当出现 devirge 时,一个是commit的提交顺序不对

    进入新分支1.git pull origin branchname 2.修改 3.git add . 4.git commit  5. git pull 6.git push 出现分歧要,就版本回退, ...

  6. Docker---(2)为什么要用Docker

    原文:Docker---(2)为什么要用Docker 版权声明:欢迎转载,请标明出处,如有问题,欢迎指正!谢谢!微信:w1186355422 https://blog.csdn.net/weixin_ ...

  7. Maven学习总结(18)——深入理解Maven仓库

    一.本地仓库(Local Repository) 本地仓库就是一个本机的目录,这个目录被用来存储我们项目的所有依赖(插件的jar包还有一些其他的文件),简单的说,当你build一个Maven项目的时候 ...

  8. amazeui学习笔记--css(常用组件2)--面包屑导航Breadcrumb

    amazeui学习笔记--css(常用组件2)--面包屑导航Breadcrumb 一.总结 1.am-breadcrumb:用am-breadcrumb来声明面包屑导航控件,.am-breadcrum ...

  9. Altium Designer线如何跟着原件走

  10. 第一个Python程序(全面)

    说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家! 一.Windows系统 1.编写Python程序方式之Sublime文本编辑器: 1>打开sublime,创建hello.p ...