动态内存申请(malloc, calloc, new)之分配虚拟内存空间和物理内存空间

动态内存申请(malloc, calloc, new)之分配虚拟内存空间和物理内存空间

1. 动态内存申请的底层系统调用

动态内存申请函数根据申请的内存大小选择不同的系统调用,小于128K选择brk系统调用分配内存,大于128K选择mmap系统调用,使用strace可以清晰追踪到系统调用。

2. 虚拟地址空间和物理地址空间

将虚拟内存地址映射到物理内存地址,叫做内存映射,映射关系缓存在一个叫页表的结构中,由MMU模块进行管理;MMU规定内存的映射最小单位,通常为4K, 每次映射,都需要关联4K或4K整数倍的内存空间。

3. malloc和new调用mmap时仅仅申请虚拟地址空间

调用malloc或new申请较大动态分配堆内存时(没有初始化操作),仅仅申请到虚拟地址空间,并不实际获得物理内存空间,因此实际的物理内存并没有消耗,只有应用进程访问虚拟地址空间时,才会触发缺页异常,才会导致内核实际去分配物理内存,并更新页表映射关系。使用下述代码进行测试(使用top和free工具):

#include 
#include  //memset
#include  //sleep
#define SIZE (80 * 1024 * 1024) //80M
int main(void)
{

    std::cout << "1---->sleep 10s" << std::endl;
    sleep(10);

    std::cout << "2---->sleep 10s end, begin malloc" << std::endl;
    char *buf = (char *)malloc(SIZE);
    //char *buf = (char *)new char[SIZE];
    if (!buf) {
        std::cout << "malloc failed" << std::endl;
        exit(1);
    }

    std::cout << "3---->sleep 10s" << std::endl;
    sleep(10);

    std::cout << "4---->sleep 10s end, begin init" << std::endl;
    memset(buf, 0, SIZE);
    std::cout << "5---->sleep 10s" << std::endl;
    sleep(10);

    std::cout << "6---->sleep 10s end, exit" << std::endl;
    return 0;
}

(1)初始时,系统剩余内存free=85M左右,应用程序占用VSZ=3M左右内存;

(2)malloc函数调用结束,系统剩余内存free=85M左右,应用程序占用VSZ=85M左右内存;说明malloc申请了虚拟地址空间,但是物理内存实际并没有分配;

(3)给申请到的空间使用memset进行初始化,系统剩余内存free=3M左右,应用程序占用VSZ=85M左右内存;说明只有应用进程去访问虚拟地址空间时,才会导致内核实际去分配物理内存;

ps: calloc和malloc的区别在于calloc分配完虚拟地址空间后马上进行初始化,这就造成实际物理内存的分配。

4.系统内存不足处理机制

(1)回收内存, 使用LRU算法进行内存页面回收;
(2)swap换入换出,不常用内存可以通过交换分区暂时写到磁盘中;
(3)OOM杀死占用大量内存的进程:当申请内存过大导致系统内存严重不足时,系统会kill掉进程,如下:
a.out invoked oom-killer: gfp_mask=0x201da, order=0, oom_adj=0, oom_score_adj=0
[] (unwind_backtrace+0x0/0xe0) from [] (dump_header.isra.13+0x4c/0x11c)
[] (dump_header.isra.13+0x4c/0x11c) from [] (oom_kill_process.constprop .16+0x38/0x1e8)
[] (oom_kill_process.constprop.16+0x38/0x1e8) from [] (out_of_memory+0x 22c/0x2a8)
[] (out_of_memory+0x22c/0x2a8) from [] (__alloc_pages_nodemask+0x4c4/0x 63c)
[] (__alloc_pages_nodemask+0x4c4/0x63c) from [] (filemap_fault+0x288/0x 3a4)
[] (filemap_fault+0x288/0x3a4) from [] (__do_fault+0x50/0x3b4)
[] (__do_fault+0x50/0x3b4) from [] (handle_pte_fault+0x22c/0x9bc)
[] (handle_pte_fault+0x22c/0x9bc) from [] (handle_mm_fault+0x9c/0xac)
[] (handle_mm_fault+0x9c/0xac) from [] (do_page_fault+0xdc/0x1c8)
[] (do_page_fault+0xdc/0x1c8) from [] (do_PrefetchAbort+0x34/0x98)
[] (do_PrefetchAbort+0x34/0x98) from [] (ret_from_exception+0x0/0x10)
Out of memory: Kill process 10040 (a.out) score 749 or sacrifice child
Killed process 10040 (a.out) total-vm:105628kB, anon-rss:96968kB, file-rss:0kB

5.总结

本文主要介绍linux系统下动态内存分配使用的系统调用mmap(大块内存使用),进而展示出虚拟地址空间和物理地址空间的映射,以及总结了进程通过 malloc等申请内存后,内存并不会立即分配,而是在首次访问时,才通过缺页异常陷入内核中分配内存。由于进程的虚拟地址空间比物理内存大很多,Linux 还提供了一系列的机制,应对内存不足的问题,比如内存回收、交换分区 swap换入换出 以及 OOM 等。

你可能感兴趣的:(内存分配,c语言,c++)