文章目录
- 1 进程内存分配探究
- 1.1 代码
- 1.2 试验过程
- 2 线程内存分配探究
- 2.1 代码
- 2.2 试验过程
- 3 总结
参考资料:
1. 嵌入式软件开发杂谈(3):Linux下内存与虚拟内存
2. 嵌入式软件开发杂谈(1):Linux下最大能创建多少线程?
在链接1中,我们可以了解到系统为每一个进程分配了4GB的虚拟内存空间,其中3GB为用户空间,是每个进程独有的,1GB为内核空间,所有的进程以及内核共同享有。
在链接2中,我们了解到系统为每个线程分配独立的堆栈,不同的系统有不同的大小,在32位linux系统上默认为8MB。
在上篇文章中介绍了系统以及进程相关的内存指标,但是有个疑问,当进程运行时候,系统是如何来分配内存的,是直接分配3GB给到内存,还是按需分配,最大3GB,通过下面代码来探究一番。
PS:下面测试环境为Ubuntu 64位系统。
1 进程内存分配探究
1.1 代码
#include <stdio.h>
#include <stdlib.h>
int main()
{
int ch = 0;
int s32Size = 1024;
char* s8Ptr = NULL;
int s32Cnt = 0;
while ((ch = getchar()) != EOF)
{
printf("get char,malloc %d mem\n", s32Size);
s8Ptr = NULL;
s8Ptr = (char*)malloc(s32Size);
if (NULL == s8Ptr)
{
printf("malloc err\n");
}
else
{
printf("malloc success, cnt:%d, addr:%p\n", ++s32Cnt, s8Ptr);
}
}
return 0;
}
上面代码,每当我们在终端输入一个字符,程序就申请1KB的内存,并且打印申请内存的地址。
1.2 试验过程
我们知道,堆是负责动态内存的分配,因此可以通过 # cat /proc/$(pid)/maps | grep heap
来查看进程的内存分配情况。
运行程序,然后使用top查看虚拟内存的使用情况以及使用上面指令来查看堆的使用情况。
# cat /proc/11031/maps | grep heap
00ae2000-00b03000 rw-p 00000000 00:00 0
从上面可以可以看到这个进程的虚拟内存使用量为4352KiB,堆的地址为00ae2000-00b03000,换算一下堆的大小为132KB。但是此时程序并没有申请内存,怎么回事?
我们第一次申请内存,打印如下:
get char,malloc 1024 mem
malloc success, cnt:1, addr:0xae2830
然后查看虚拟内存VIRT使用量,还是4352KiB,并没有增加,堆的使用地址还是00ae2000-00b03000,也没有改变。
继续申请内存:
get char,malloc 1024 mem
malloc success, cnt:2, addr:0xae2c40
get char,malloc 1024 mem
malloc success, cnt:3, addr:0xae3050
第二次和第三次申请内存,VIRT和堆的信息仍然维持原状,没有改变,继续申请:
get char,malloc 1024 mem
malloc success, cnt:128, addr:0xb02c20
直到第128次申请内存时候,VIRT的使用量为4484KiB,比4352增加了132KB,而堆的使用量为264KB,比上次增加了132KB,信息如下:
cat /proc/11031/maps | grep heap
00ae2000-00b24000 rw-p 00000000 00:00 0 [heap]
继续申请内存,直到第258次申请内存,VIRT变为4616K,比4484增加了132KB,而堆的使用量为396KB,比上次增加了132KB,信息如下:
get char,malloc 1024 mem
malloc success, cnt:258, addr:0xb23c40
# cat /proc/11031/maps | grep heap
00ae2000-00b45000 rw-p 00000000 00:00 0 [heap]
从上面的测试中,我们看到第二次申请内存的地址为0xae2c40,而第一次申请内存的地址为0xae2830,两者相减,为1040,但是我们只申请了1024个字节,为什么会多16个字节?
第一个问题:为什么系统分配的内存比实际申请的内存大16个字节?
然后,我们可以看到,当程序运行时候,系统已经先为程序分配了132KB的内存,在随后我们申请内存时候,一直使用的是系统预先申请的132KB内存,直到我们申请的内存超过132KB,然后系统再次申请132KB,而不是我们需要多少就申请多少?
第二个问题:为什么系统会给进程申请132KB的内存,而不是我们真正需要的内存?
2 线程内存分配探究
2.1 代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/prctl.h>
void *fun(void *arg)
{
printf("----->thread_test\n");
prctl(PR_SET_NAME, "thread_test");
int ch = 0;
int s32Size = 1024;
char* s8Ptr = NULL;
int s32Cnt = 0;
while ((ch = getchar()) != EOF)
{
printf("get char,malloc %d mem\n", s32Size);
s8Ptr = NULL;
s8Ptr = (char*)malloc(s32Size);
if (NULL == s8Ptr)
{
printf("malloc err\n");
}
else
{
printf("malloc success, cnt:%d, addr:%p\n", ++s32Cnt, s8Ptr);
}
}
}
int main()
{
int s32Ret = 0;
pthread_t thread;
if (getchar() != EOF)
{
s32Ret = pthread_create(&thread, NULL, fun, NULL);
printf("pthread_create, ret:%d\n", s32Ret);
}
while(1) sleep(10);
return 0;
}
上面代码,当我们第一次输入一个字符,则创建线程,随后再次输入字符则是分配内存
2.2 试验过程
运行程序,查看VIRT为6520KB,如下:
maps信息如下:
然后我们创建线程,查看VIRT和maps信息如下:
可以看到,VIRT由6520增加到14716,即增加了8MB,这个8MB是系统为每个线程创建时分配的。
然后开始第一次分配内存,VIRT和maps信息如下:
可以看到,当我们第一次申请内存的时候,VIRT由14716增加到80252,即系统为线程申请了64MB内存,而不是给进程分配的132KB内存。
第三个问题:为什么系统会给线程申请64MB的内存,而不是我们真正需要的内存?
开始第二次申请内存,VIRT和maps数据均没有变化,和进程中的分配机制一样,先从已经分配的内存中使用,当超过已经分配的内存时,才会重新分配新的内存。
get char,malloc 1024 mem
malloc success, cnt:1, addr:0x7f9bac0008c0
get char,malloc 1024 mem
malloc success, cnt:2, addr:0x7f9bac000cd0
上面是程序的打印信息,可以看到我们申请了1024字节的内存,但是系统还是分配了1040个字节的内存,即多分配了16字节的内存,和问题1一致。
3 总结
通过上面的测试,我们得出了三个问题:
- 第一个问题:64位系统,为什么系统分配的内存比实际申请的内存大16个字节?
- 第二个问题:64位系统,为什么系统会给进程申请132KB的内存,而不是我们真正需要的内存?
- 第三个问题:64位系统,为什么系统会给线程申请64MB的内存,而不是我们真正需要的内存?
后面章节将解决这几个问题。