【问题标题】:Memory allocation responsibility内存分配责任
【发布时间】:2012-05-05 03:00:55
【问题描述】:

我一直在思考这个问题,但还没有找到任何解释。

哪个模块决定应该分配多少内存,比方说,一个 C++ 程序?是操作系统决定编译器的推荐吗? ..链接器?

而堆栈和堆在分配的内存中的比例是多少?

【问题讨论】:

    标签: c++ memory-management operating-system


    【解决方案1】:

    对于不同的操作系统,答案是不同的。通常,可执行文件包含主线程所需的堆栈大小,由链接器放置在那里,可能会被操作系统设置覆盖。操作系统设置可以通过一种或多种方式进行配置,可能针对每个用户。一些操作系统不需要预先指定堆栈大小,它们可以在使用堆栈时或多或少无限期地添加堆栈(直到达到硬限制或系统耗尽可用内存)。那些确实需要预先确定大小的人最初可能只分配地址空间而不是内存,并在堆栈达到那个距离时将地址映射到内存。

    堆通常不会预先分配,因此没有“堆栈和堆的比例”。分配给进程的总内存可能会受到限制,也可能不会受到限制——如果没有,那么它可以达到系统资源允许的上限,或者在 32 位系统上可能会受到可用地址空间的限制。

    【讨论】:

      【解决方案2】:

      这不是 C++ 标准中的问题。它依赖于编译器和操作系统。

      有关链接器产生的、操作系统在确定程序请求的资源时会考虑的类型的示例,请参阅:

      http://en.wikipedia.org/wiki/Executable_and_Linkable_Format#ELF_file_layout

      在某些情况下,有专门从操作系统请求资源的 API:

      Change stack size for a C++ application in Linux during compilation with GNU compiler

      还有一些方法可以告诉操作系统在某些环境中设置配额和限制:

      https://stackoverflow.com/questions/4983120/limit-memory-usage-for-a-single-linux-process

      Set Windows process (or user) memory limit

      如果您想对某个操作系统如何管理资源使用进行实证研究,您可能会通过进程监视器实用程序来更好地了解它,而不是寻找文档......特别是。使用闭源操作系统。

      【讨论】:

        【解决方案3】:

        取决于您的程序和操作系统。通常,在启动时只分配足够的内存来保存可执行文件、任何只读数据,通常大约 4k 用于堆栈。然后,当您调用 malloc 或 new 来分配内存时,您将获得虚拟内存空间,而无需任何物理内存对其进行备份。这称为延迟分配,只有在您实际写入时才会物理分配内存。

        编译并计时以下内容以了解我在说什么:

        //justwrites.c
        #include <stdlib.h>
        
        int main(int argc, char **argv) {
        
        int *big = calloc(sizeof(int),19531); // number of writes
        
        return 0;
        }
        
        // deadbeef.c
        #include <stdlib.h>
        
        int main(int argc, char **argv) {
        
        int *big = malloc(sizeof(int)*20000000); // allocate 8 million bytes
        // immediately write to each page to simulate all at once allocation
        // assuming 4k page size on 32bit machine
        for ( int* end = big + 20000000; big < end; big+=1024 ) *big = 0xDEADBEEF ;
        
        return 0;
        }
        
        // bigmalloc.c 
        #include <stdlib.h>
        
        int main(int argc, char **argv) {
        int *big = malloc(sizeof(int)*20000000); // allocate 80 million bytes
        return 0;
        }
        

        【讨论】:

        • 鉴于 Windows (AFAIK) 不这样做,乐观内存分配是否可以称为“典型”?
        • @Steve:MSVC++ 没有。 Windows 确实支持 Linux/libc malloc 使用的相同底层方法(即 CreateFileMapping 没有文件句柄,也没有特殊选项),其他 Windows 编译器肯定可以通过这种方式实现 malloc
        • @MSalters:是的,但是如果您没有 OOM 杀手,那么该技术就不那么好了。而且您可能不希望编译器将 OOM 杀手的用户模式版本粘贴到每个应用程序中。是否有任何 Windows 编译器(包括 mingw)实际上进行了乐观分配,如果是,那么成功的程度如何?
        • @SteveJessop:Windows 的行为会更具确定性:如果承诺的存储空间被实际占用并且交换文件无法增长,则负责分页的结构化异常处理程序将失败,因此应用程序尝试使用内存会因未处理的异常而退出。不知道实际的编译器实现,抱歉。
        【解决方案4】:

        至少对于 32 位 Windows,每个进程都有自己的地址空间副本,2G 用户 2G 内核(由所有进程共享),虚拟内存子系统确保访问相同位置的进程获得适当的数据他们的过程。这就是一个程序可以具有相同的入口点并多次运行而不踩踏具有相同可执行文件的其他进程正在使用的数据的方式。

        应用程序将继续使用更多的虚拟内存,内核会为该进程分配更多的物理内存,直到物理内存、交换空间/分页文件用完为止。您可以通过系统调用限制进程可以使用的内存。

        堆栈和堆几乎总是分配在可用内存的两端,因此堆栈从可用内存的顶部向下增长,而堆从底部向上增长(这个决定取决于架构)。这允许它们单独增长,以便需要大量堆但不需要太多堆的程序可以使用与需要大量堆但不需要太多堆的程序相同的计划。

        【讨论】:

          猜你喜欢
          • 2015-05-07
          • 1970-01-01
          • 1970-01-01
          • 2013-09-11
          • 1970-01-01
          • 1970-01-01
          • 2013-02-09
          • 2017-02-12
          • 2013-02-19
          相关资源
          最近更新 更多