【问题标题】:why do you have to malloc large data structures on the heap?为什么必须在堆上 malloc 大型数据结构?
【发布时间】:2021-07-08 17:58:49
【问题描述】:

我记得说过堆栈内存地址是如何增长的,而堆是如何增长的,但它们占用了相同的内存条。如果是这样,为什么一些大型数据结构会导致堆栈溢出,但在堆上却完全没问题?

【问题讨论】:

  • stck 传统上是有限的,而堆可以利用整个内存。
  • @Devolus,这与传统上堆栈内存地址增长和堆增长的 OP(正确)观察相矛盾。所以他们可以在两者之间的任何地方见面。
  • @PaulOgilvie,为什么会有矛盾?当您用完堆栈空间时会发生堆栈溢出,但是这是确定的。它与可用内存无关,因为这取决于架构。
  • 有限的堆栈大小是操作系统的功能,而不是语言或底层硬件(通常)。这样做的动机是确保没有一个进程在出现恶意(例如通过编写糟糕的递归调用)并威胁到整个系统的稳定性时获取所有可用内存。当然,如果你在堆上发疯,同样的事情也会发生,

标签: c stack malloc stack-overflow heap-memory


【解决方案1】:

首先,忽略您所听到的历史上的过度简化,您需要将malloc 用于大型对象而不是自动(“堆栈”)存储有一个根本原因:只有malloc 具有可以报告的接口内存不可用。如果您在其主体中输入带有char foo[1000000000]; 的函数,则在基于实际堆栈的实现中,是的,堆栈指针将被调整 1000000000 但那时谁知道它是否指向您可以用作堆栈的一部分的空闲内存或在其他一些不相关的记忆之上。一些实现(GCC 的-fstack-clash-protection)会一次调整一个页面并探测每个页面以在第一个保护页面上产生错误,从而使您的程序崩溃而不是破坏不相关的内存,但这不是一个理想的结果。只要有可能发生故障,您就需要一个可以报告和处理故障的渠道。

现在,关于为什么历史上的“堆长大,堆栈长大”的解释是错误的:

  1. 只有在只有一个堆栈时才有效。在多线程程序中,每个线程都有一个堆栈。如果您想将它们全部定位,以便它们可以大幅增长,那么您几乎无法在 32 位内存空间中创建任何线程。在 64 位系统中,这将是可能的,只是因为空间很大,但存在问题(而且大多数 64 位系统实际上并没有 64 个可用的地址位)。

  2. “堆”和“栈”不是你内存空间中唯一的东西,“堆”甚至不是一个有意义的概念。是的,历史上的malloc 实现对大多数分配使用了一个不断增长的固定“堆范围”,但通常会免除非常大的分配,而是为每个分配执行单独的mmapmunmap。但更重要的是,其他东西也存在于这个空间中——你的程序的可执行代码、共享库的可执行代码和数据段等。在现代系统上,放置它们的位置是随机的,以减轻程序错误的后果。这意味着它们可以放置在您认为的“堆顶”和“栈底”之间的任何位置,从而限制了它们可以连续增长的距离。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多