前一阵子在做对输入法移动端服务器失败率监控的过程中使用C++编写了一个脚本。由于监控脚本就是一个死循环,希望可以长时间运行。但是没运行2天多,监控程序就失效了。到虚拟机上一看原来是监控脚本已经在虚拟机上崩溃,通过分析崩溃文件大致原因是因为内存泄漏的问题。然后观察机器的运行过程发现脚本每次循环的时候都会有大搞90kb的内存泄漏。
查看监控程序的源码:
啥也没有啊?就是创建了一个线程,干完活后释放掉了。为什么会有这么大的内存泄漏呢?
初步怀疑是createThread的问题,查了一下输入法的代码大量使createThread的情况,难道是它,不会吧!
在网上搜了一下createThread内存泄漏,果然存在这种情况。内存泄露存在的原因,是由CreateThead创建的线程不会去检查CRT的TLS数据是否需要释放。
在了解了CreateThread导致内存泄露的原因后,我简单考虑了一下避免这种内存泄露的方法。
首先老老实实的使用_beginthead(ex)函数,是最稳妥的办法。
其次能否避免使用CRT中依赖TLS的函数呢?
也许可以,但是我们所书写的代码,不完全是我们自己在用,而且我们CreateThead所创建的线程,也不一定跑的都是自己的代码,例如我们提供一个库给别人使用,难道还要特别说明不允许使用errno/localtime等函数么?因此这个方法是不建议的。
如果我想用CreateProcess,或者我所使用的底层库使用的是CreateProcess函数,我又不可避免的会使用依赖于TLS的VC运行时库函数,有什么办法能保证ptd会被释放呢?
我们可以自己释放ptd。前面的分析可以看出,_endthread()函数调用了_freeptd(ptd)来释放ptd,因此我们可以在线程函数的末 尾显示的调用_endthread()或_endthreadex(retcode)函数来释放ptd。在查看了_freeptd函数的代码后,我发现如 果传入参数是NULL,_freeptd函数释放的就是caller线程的ptd,因此也可以直接调用_freeptd来执行清理。
另外,我们也可以自动释放ptd。在VC的工程属性中,可以选择运行时库的类型,如图:
如果我们选择/MTd或/MDd,运行时库以动态方式链接,即我们的程序会使用传说中的msvcrt*.dll。在这个dll的入口函数中,会在 DLL_THREAD_ATTACH时为attach的线程初始化TLS数据,在DLL_THREAD_DETACH时为detach的线程调用 _freeptd函数执行清理。因此如果我们使用VC的动态库,使用CreateThread和使用_beginthread是同样安全的。
总结一下,避免CreateThread引发泄露,大致有几种方法:
1. 使用_beginthread/_beginthreadex函数创建线程
2. 在线程函数return前,显示调用_endthread/_endthreadex函数
3. 在线程函数return前,显示调用_freeptd(NULL),此方法在C语言中有效
4. 使用/MTd或/MDd参数
如需转载该篇文章,请注明来自“搜狗测试”