02

您所在的位置:网站首页 socket内存占用 02

02

2023-09-24 04:47| 来源: 网络整理| 查看: 265

一 业务场景分析

因为多线程在平时是非常常见的,最近有点空,想自己写个线程池而不是用别人写好的。 所以开撸,发现当我在调试一个线程池时,发现使用了一两个小时后,虚拟内存占用得非常高。然后我开始分析,一开始我先写了一个不带调整线程的线程池,发现线程池开启几个小时后,虚拟内存都是很稳定,基本也就正常的几百m。而当我添加了调整线程后,发现同样一个小时多后,虚拟内存变得非常的大,达到20g左右。 注:这里调整线程的作用是:可以根据任务数的大小自动调整线程池中线程的数量,所以就涉及线程的添加和销毁。

1 测试

经过上面发现问题后,肯定想排查问题,于是开启测试。 这是我测试的情况:

这是添加调整线程的情况,可以看到经过一两个小时后,虚拟内存都变得非常的大。 在这里插入图片描述 在这里插入图片描述 下面是C语言写的带调整线程的线程池,情况和上面C++的一样。 在这里插入图片描述 在这里插入图片描述

然后我就把调整线程的代码去掉,发现果然情况变得不一样,经过类似的时间,虚拟内存仍然是一样的,非常稳定。 在这里插入图片描述 在这里插入图片描述

2 写测试代码

由于线程池添加了调整线程这些内容,测起来不方便,于是就自己写个程序测试。

#include #include #include #include #include void *thread1(void *arg) { printf("thread1\n"); return NULL;//创建线程但不回收 } int main(){ getchar(); printf("ok\n"); pthread_t tid1; while (getchar()) { printf("start create thread.\n"); pthread_create(&tid1, NULL, thread1, NULL); } return 0; } 首先运行程序 ./main 然后查看该进程的pid。我的是22651。 ps -ajx | grep main

在这里插入图片描述

使用top观察虚拟内存。一开始是15428,大概15M左右。 top -p 22651

在这里插入图片描述

然后查看开辟一个线程所占用的堆栈大小。 ulimit -a //查看stack size那一行 或者 ulimit -s

下面看到是8m。 在这里插入图片描述 单位需要从ulimit -a查看,单位是kb。 在这里插入图片描述

好了,目前我们可以开始调程序观察了。 1)下面看到,由于我不小心按到其它键,创建了两个线程。然后查看VirMem大小,大小为31820,刚好和15428 + 2*8192=31812差不多相等。 在这里插入图片描述 在这里插入图片描述

2)然后再创建线程,按下enter即可。同样看到VirMem=40016,刚好和31812 + 8192=40004差不多相等。 在这里插入图片描述 在这里插入图片描述

3)然后再创建线程,按下enter即可。同样看到VirMem=48212,刚好和40004 + 8192=48196差不多相等。 在这里插入图片描述 在这里插入图片描述

4)然后你不断重复创建线程,情况都和上面一样。所以我们可以分析程序了。

程序可以看到,当我们创建线程后,并未对线程进行回收,而是直接return了。所以我们只需要证明调用join回收后,观察虚拟内存的情况,若未增加,就验证了我们的猜想,是没有回收导致的,否则就可能需要从其它方面入手。

更改代码,添加一个调整线程去回收退出后的线程,观察情况。 先列出代码:

#include #include #include #include #include #include using namespace std; vector garbage;//垃圾回收,记录退出线程的tid,以便join回收 pthread_mutex_t mymutex;//锁住垃圾队列 void *thread1(void *arg) { printf("thread1\n"); pthread_t t = pthread_self(); pthread_mutex_lock(&mymutex); garbage.push_back(t); pthread_mutex_unlock(&mymutex); return NULL;//创建线程但不回收 } //调整线程,回收资源 void *adjust(void *arg) { while(true) { sleep(2);//定时回收 pthread_mutex_lock(&mymutex); int size = garbage.size(); vector::iterator it = garbage.begin(); if(garbage.size() >= 0){ for(it; it != garbage.end(); it++) { pthread_join(*it, NULL); } garbage.clear(); } pthread_mutex_unlock(&mymutex); printf("清理完本次退出的线程,个数为=%d\n", size); } pthread_exit(NULL); } int main(){ pthread_mutex_init(&mymutex, NULL); //先创建一个调整线程,用于定时回收资源 pthread_t adjust_tid; pthread_create(&adjust_tid, NULL, adjust, NULL); getchar(); printf("ok\n"); pthread_t tid1; while (getchar()) { printf("start create thread.\n"); pthread_create(&tid1, NULL, thread1, NULL); } pthread_mutex_destroy(&mymutex); return 0; }

同理,安装上面一开始的情况,去top -p pid去观察创建线程后的Virt。 我的情况是,当虚拟内存到达一定情况后,一般是几百M,和你程序初始有关,我这里到达187m左右,无论你开辟多少线程,过了2s内被回收后,最终还是变回187m。也就是说,Virt的大小始终保持稳定。

我的测试情况: 1)下图是我创建一定线程并回收后,最终稳定的情况值。 在这里插入图片描述 2)然后我们再程序的界面上,不断的按下enter,main主线程就会创建多个线程,此时top的Virt可以看到秒增加变大。 在这里插入图片描述

3)然后回收后,立马回到187m。 下图由于比较难截图,只能使用windows自带的截图,比较模糊但是还是能看的,回收的线程数是36。 在这里插入图片描述

所以由上面的测试结果可以看到,导致虚拟内存变高的原因是我们线程结束后,未使用join进行回收。

实际上,我们测试时可以使用detach去代替的。情况和上面类似,只不过最终稳定的值比上面低点,大概24m左右。

#include #include #include #include #include #include using namespace std; void *thread1(void *arg) { pthread_detach(pthread_self()); printf("thread1\n"); return NULL;//这里等价于pthread_exit(); } int main(){ getchar(); printf("ok\n"); pthread_t tid1; while (getchar()) { printf("start create thread.\n"); pthread_create(&tid1, NULL, thread1, NULL); } return 0; } 3 总结

虚拟内存非常高,导致的情况可能是非常多的,我这里是因为添加调整线程后,部分退出的线程pthread_exit退出后,未调用join进行回收,导致在虚拟内存占用高,但是在实际的物理内存中,它的值是正常的。 虽然部分场景下,只有物理内存维持正常不爆增,虚拟内存过高还是可以让程序正常进行的,但是程序员必须防范于未然,而且看着也难受,既然找到bug,后续就自己动手改代码咯。

关于其它出现虚拟内存占用高的情况的文章: 为什么linux下多线程程序如此消耗虚拟内存



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3