• 【VS开发】malloc申请内存错误分析


    每个进程会有4G的虚拟地址空间, malloc得到的的地址都是虚拟地址, 并且当malloc的时候, 操作系统并不会将实际的内存分配给进程的, 所以malloc只会占用进程自身的虚拟地址空间。
    我以前也做过申请内存的测试,并且写了一个短文:

    操作系统: Redhat Linux AS5 32bit
    服务器内存: 4G
    服务器类型: I32

    最近写搜索引擎, 因为创建索引需要大量的内存, 所以对Linux下的大内存申请进行了一些测试.

    (1)char * p = (char *)malloc( 2G字节 );
    =>申请失败.
    (2)char * p = (char *)malloc( 1.9G字节 );
    =>申请成功
    (3)连续的申请10个300M的内存空间
    for ( i=0; i<10; i++ )
    p = (char*)malloc(300M字节)
    =>前9次成功, 最后1次申请失败
    (4)先申请1.9G, 再申请900M
    p = (char *)malloc( 1.9G字节 );
    p = (char *)malloc( 900M字节 );
    =>两次申请都成功.

    我的理解如下:
    对于在普通默认的2.6.*的linux内核!
    32位的机器里, 一个进程的内存地址空间范围是0-3G共4个G, 其中最后一个G是内核态的地址空间, 所以给用户态的内存地址空间只留下了前3个G. 那么这样, malloc能够申请到3G以内的内存才对, 但是结果并非如此.在(1)中我们申请2G的内存都没有申请到, 这是什么原因呢?先让我们看一看实际上进程的4G内存空间都放着或被map着什么:

    第0G和第1G:用户态地址空间
    第2G:库函数映射等
    第3G:内核态内存空间

    用户态地址空间中还包含了进程代码本身占用的地址空间, 栈的空间等等.
    第2G中, 库函数映射等只占用了很少的一部分空间,还有很多的空闲空间.

    现在让我们解释这4个问题:
    第(1)个问题, 由上图可以看出, 没有连续的2G的内存, 所以申请2G的连续内存是肯定失败的.
    第(2), 申请1.9G的空间是成功的, 这是因为前两个G可能会有1.9G的连续空间.
    第(3), 申请了300M*9 = 2.7G是成功的, 是的, 前3G中有可能空间着2.7G的空间, 前两个G中空闲的加上第3个G中空闲的部分. 但是如果一次申请2.7G是不行的, 因为没有连续的2.7G的地址空间. 最后一个300M没有申请成功的原因是, 申请的空间大小不能超过3G的用户态地址空间.

    第(4), 比较有意思, 显然那个1.9G是在第1-2G这个地址空间中申请成功的, 后900M是第3个G这片地址空间中申请成功的. 我们一共申请到了2.8G的"内存", 却也不是连续的


    续:

    相信C/C++程序员都用过这个库函数, 这个函数时程序员申请堆中的内存,需要自己手动释放内存,所以这个函数也是Memory Leak的根源。但是malloc一次最多能申请多少内存呢,显然这个跟我们物理内存的大小和
    我们的系统,编译器都有一定的关系。已经不记得之前在哪里遇到过这个问题,今天忽然想起来了,于是自己做了个实验。
    我的开发环境是Windows7 64位,内存8G,IDE是codeblocks,支持开源,下面是测试代码:

    点击(此处)折叠或打开

    1. #include <iostream>
    2. #include <stdio.h>
    3. #include <stdlib.h>
    4. int64_t maximum = 0;


    5. using namespace std;
    6. int main()
    7. {
    8.     cout<<"sizeof(void*) is:"<<(int)sizeof(void*)<<endl;
    9.     cout<<"sizeof(int) is:"<<sizeof(int)<<endl;
    10.     int64_t blocksize[] = {1024*1024, 1024, 1};
    11.     int64_t i, count;
    12.     for(i=0;i<3;i++)
    13.        {
    14.         maximum = 0;
    15.         for(count=1;;count++)
    16.             {
    17.                 void *block = (void*)malloc(maximum + blocksize[i] * count);
    18.                 if(block)
    19.                     {
    20.                         maximum = maximum + blocksize[i] *count;
    21.                         free(block);
    22.                     }
    23.                 else
    24.                     {
    25.                         break;
    26.                     }
    27.             }
    28.          cout << "maxmium malloc size:"<<maximum/1000000<<"M"<<endl;
    29.          }


    30.     cout << "Hello world!" << endl;

    31.     return 0;
    32. }

    程序输出如下:

    这个结果是不是很蹊跷,我64位的系统,而且8G的内存,占用了很少,但是这里为什么只分配了2G不到呢,我原来的推想是操作系统保留一部分内存(大概2G),还有差不多6G可以申请,所以这个结果让我很惊讶。于是我又在64bit的linux下,内存4G,用同样代码进行了测试,这次输出是


    这次输出似乎跟预想差不多,4G内存能分配3.5G左右,那么windows下是什么问题导致我8G的内存只能malloc 2G不到呢。细想一下,觉得肯定是编译器的问题,果然我的codeblocks默认的编译器是mingw-32-g++,可能细心的读者已经看到前面的sizeof(void*)的值是4,也就是说在windows下codeblock下默认将该程序编译成32位的了,所以才会出现分配2G不到的内存,感兴趣的可以用其他的64位的编译器测试一下,应该会得到正常值。

     

    总结一下:

    程序是32位或者是64位并不是由你的操作系统决定,而是编译器决定,准确的说是决定于编译器和编译选项,64位系统照样可以跑32位的程序。







  • 相关阅读:
    如何确定Kafka的分区数、key和consumer线程数
    Storm程序永久代内存溢出
    JVM堆内存相关的启动参数:年轻代、老年代和永久代的内存分配
    Git添加本地项目出现fatal: unable to get credential storage lock: File exists
    Kafka的通讯协议
    文件上传 数据对比
    jquery 弹出层
    XSS 简单理解之:AntiSamy
    XSS 简单理解
    超人说我最叼蝙蝠侠笑了 超级英雄战力排名
  • 原文地址:https://www.cnblogs.com/huty/p/8518846.html
Copyright © 2020-2023  润新知