02 |
您所在的位置:网站首页 › socket内存占用 › 02 |
一 业务场景分析
因为多线程在平时是非常常见的,最近有点空,想自己写个线程池而不是用别人写好的。 所以开撸,发现当我在调试一个线程池时,发现使用了一两个小时后,虚拟内存占用得非常高。然后我开始分析,一开始我先写了一个不带调整线程的线程池,发现线程池开启几个小时后,虚拟内存都是很稳定,基本也就正常的几百m。而当我添加了调整线程后,发现同样一个小时多后,虚拟内存变得非常的大,达到20g左右。 注:这里调整线程的作用是:可以根据任务数的大小自动调整线程池中线程的数量,所以就涉及线程的添加和销毁。 1 测试经过上面发现问题后,肯定想排查问题,于是开启测试。 这是我测试的情况: 这是添加调整线程的情况,可以看到经过一两个小时后,虚拟内存都变得非常的大。 然后我就把调整线程的代码去掉,发现果然情况变得不一样,经过类似的时间,虚拟内存仍然是一样的,非常稳定。 由于线程池添加了调整线程这些内容,测起来不方便,于是就自己写个程序测试。 #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下面看到是8m。 ![]() ![]() 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)下图是我创建一定线程并回收后,最终稳定的情况值。 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 |