在堆栈上分配 16kb 是否太多了?
Posted
技术标签:
【中文标题】在堆栈上分配 16kb 是否太多了?【英文标题】:Is it too much to allocate 16kb on the stack?在堆栈上分配 16kb 是不是太多了? 【发布时间】:2011-07-31 05:01:12 【问题描述】:我需要在调用 c 函数之前实例化一个 char[16384] 缓冲区。函数返回后,我会读取它的某些部分并丢弃它。
在栈上分配好还是应该用堆?
编辑:我将添加一些信息。该代码将在多个平台上运行,从 PC 到 iPhone,我猜堆栈空间不会那么大,但我不知道。
【问题讨论】:
什么系统,为什么不用malloc? 可能没问题,但取决于您的平台以及程序中发生的其他情况。 视情况而定。您始终可以在常规 x86(-64) 上更改堆栈大小。嵌入式可能会有所不同 我不会说太多。但无论如何都要把它放在堆上,因为那里更难滥用缓冲区溢出。 这段代码是可重入的吗?有多少线程将调用函数/方法? 【参考方案1】:很难对这个问题给出明确的肯定或否定答案,因为答案在很大程度上取决于您的环境以及在程序中的哪个点调用分配内存的函数。
个人而言,如果我在代码审查中看到这一点,我会提出一个危险信号。这是用于基于堆栈的缓冲区的大量内存。它今天可能在你使用它的非常特定的地方工作,但明天当你被跟注时你下面有更大的筹码怎么办?或者当客户遇到您没有考虑到的情况时?
但就像我说的那样,它取决于场景,它可能适合您的特定场景。您的问题中根本没有足够的细节来回答是或否
【讨论】:
+1:使用如此多堆栈空间的代码在具有深度嵌套的大型程序中无法可靠地重用。 除非您可以容忍malloc
失败,否则堆栈上的分配可能更可靠。除非你使用递归(被认为是有害的,至少当它超出O(log n)
时),静态分析可以确定程序所需的堆栈空间总量,并证明你永远不会发生堆栈溢出。另一方面,malloc
可能会在很多情况下失败,其中大多数情况在大多数程序中都是依赖于数据的,不能排除。当需要一个小的有界缓冲区时,我会使用堆栈分配,只要你可以确定它是安全的。
@R..:这有点像在人行道上骑自行车实际上比在路上更安全。当所有骑自行车的人随后都使用路面时,这种自我破坏//静态分析在多态性、一流函数对象和元编程/运行时代码生成等方面变得不那么有用。上次我检查 C++ 的静态分析时或多或少地停在“太难可靠解析”阶段(即,除非在 IDE 中非常紧密地集成,否则几乎不可能确信用于分析的实际预处理源与编译源相匹配)。
@R..:“当需要一个小的有界缓冲区时,我会使用堆栈分配”。当然,任何人都会。问题是16k是否“小”。我使用的系统默认堆栈大小低至约 4k,因此 IMO 提问者需要找出他实际需要支持的系统,而不是让我们猜测 ;-)
@Steve:这就是我在回答中提到的。 :-) 除了我提到的少数情况外,16k 非常小。【参考方案2】:
除非您正在为嵌入式系统编程、可能从主线程以外的线程运行的代码或递归调用的代码,否则我会说 16k 完全在您可以在堆栈上分配的合理大小范围内。
至于线程,如果你使用 POSIX 线程并且希望你的程序是可移植的,你可以使用pthread_attr_setstacksize
接口来指定你的线程需要的堆栈空间量,然后只要你知道调用模式和高估选择尺寸,你可以肯定它是安全的。
【讨论】:
【参考方案3】:完全取决于您的操作系统和进程定义。最好通过malloc
从堆中分配它并检查结果(可能会失败)。在堆栈上分配失败可能会导致堆栈损坏,您将无法在运行时捕获。
【讨论】:
我从未见过堆栈分配失败导致堆栈损坏的情况;这就是为什么我们得到可怕的“堆栈溢出”并且程序只是停止。 @Mark Ransom,那么您可能只在具有MMU 的系统上工作过,该系统可以轻松地在堆栈和文本或数据段之间插入保护页。 :) 较小的系统并不总是提供堆栈和文本或数据之间的强制分离。 @Mark:请参阅几个月前在 Chrome 漏洞赏金系统中发现的 glibcfnmatch
漏洞......
@sarnold,作为函数序言的一部分,即使是较小的系统也可以轻松地检查堆栈指针是否有限制。我可以看到它可能是可选的。【参考方案4】:
如果您使用的是 C++(因为问题有该标签),请使用 vector<char> buffer(16384)
- 这样您会自动解除分配,但大缓冲区会在堆上分配。
唯一的潜在缺点是缓冲区将被默认初始化。这可能是您负担不起的可能性很小(尽管它可能没有任何影响)。
【讨论】:
反正我不需要自动解除分配。如果我决定在堆上分配它,我将在 malloc/new 之后几行在同一个函数中执行 free/delete。 @Damian 免费手册并非异常安全且容易出现未来开发人员错误【参考方案5】:我会说这取决于缓冲区的预期寿命。
如果打算让缓冲区仅存在于创建函数及其调用的函数的范围内,那么基于堆栈是避免内存泄漏的绝佳机制。
如果打算让缓冲区长寿命,超过创建函数的范围,那么我会malloc(3)
缓冲区。
我的pthread_attr_setstacksize(3)
说查看pthread_create(3)
以获取有关默认堆栈大小的详细信息;可悲的是,我的系统上只有 POSIX 提供的 pthread_create(3posix)
联机帮助页,它缺少这些细节;但我记得默认堆栈大小是如此之大,以至于大多数想知道如何设置堆栈大小的人都想缩小它,这样他们就可以在给定的内存量中运行更多线程。 :)
【讨论】:
在 Linux/glibc 上,线程的默认堆栈大小通常是巨大的,但在其他实现上,默认值可能非常小。这就是为什么我说打算移植的代码应该总是设置一个堆栈大小属性,除非它只使用少量的堆栈空间(在这种情况下它不关心堆栈是否太小)并且只使用少量的线程(在这种情况下,它不关心堆栈是否太大)。【参考方案6】:如果您的代码没有被多个线程使用并且它不是可重入的......那么我只会在该缓冲区的程序初始化时执行一个 malloc。您将不必担心围绕堆栈大小的架构问题。您绝对不想每次调用都执行 malloc/free。
【讨论】:
继承此代码的人会恨你使用全局数据。顺便说一句,如果您确实坚持这样做,请不要使用malloc
。一个简单的全局数组也可以,它确保在输入main
之前保留内存(因此您不必处理失败)。以上是关于在堆栈上分配 16kb 是否太多了?的主要内容,如果未能解决你的问题,请参考以下文章