“pragma pack 1”能够帮助避免堆碎片吗?

11

在我的程序中,我发现一些驻留大小增加。我认为这是由于堆碎片化引起的。因此,我计划使用 #pragma pack 1。这样做会减少堆碎片化吗?
是否会产生其他额外开销?
我该不该这样做?


11
如果堆分配器尝试在良好的本地字边界上进行分配,并且结构体不再填充到良好的本地字边界,则实际上可能会增加堆碎片化。 - Some programmer dude
6
访问未对齐的内存会导致性能下降,这可能比您想要实现的效果更糟(减小对象大小)。此外,您有没有检查内存泄漏,而不是假设是堆碎片问题? - Bgie
嗯,我正在使用Valgrind来查找内存泄漏。在Valgrind报告中,我没有看到任何泄漏。但是RSS正在增加,这本身就是一个问题。 - piyush
5
我建议使用特定的分配器来避免碎片化问题。 - Jarod42
5个回答

10

有一种被证明很有效的技术叫做内存池。它专门设计用于减少内存碎片和帮助解决内存泄漏问题。在程序功能受到内存碎片影响时,应该使用它。

'pragma pack 1'无法帮助避免堆内存碎片。

'pragma pack 1'用于消除结构体中的填充字节,以帮助在程序之间传输二进制结构。


好的建议,虽然我想知道内存池如何帮助解决内存泄漏问题? - lethal-guitar
致命吉他,这与C语言有关,而不是C++。假设您想要确保客户端从Web服务器断开连接后没有内存泄漏。如何实现?当客户端连接到服务器时,您创建一个内存池(使用OS malloc分配一个大数组)。在处理客户端代码时,您要求内存池为对象分配内存,而不是OS。然后,客户端从服务器断开连接,您释放整个内存池(使用free)。有关示例,请搜索“APR内存池”和“pjsip快速内存池”。请参见http://dev.ariel-networks.com/apr/apr-tutorial/html/apr-tutorial-3.html。 - alexander
4
它也是心脏出血漏洞的起源地。;-) - DevSolar

6

这仅仅是操作系统的工作原理。当你释放已经分配的内存时,它并没有从进程内存映射中被删除。 这是操作系统的一种优化,以防该进程需要再次分配内存,因为那样操作系统不必向进程内存映射添加新的映射。


5
#pragma pack N指示编译器按照特定方式对结构的成员进行对齐,使用(N-1)字节填充。例如,如果N为2,则每个char将占用2个字节,一个被赋值,一个是填充。当N为1时,就不会有填充。这将导致更多的碎片化,例如当该结构具有一个char和一个int时,共计5个字节,则会存在奇数字节数量。 参考:#pragma pack effect

1
对于默认的包处理,结构中的单个字符变量不会填充,但较大的类型将填充到与其类型相对应的边界,除了8字节类型(如双精度或长整型)可能不会放置在8字节边界上,如果默认的包值为4。 - rcgldr

4
装箱结构可能不会对堆碎片产生太大影响。堆碎片通常发生在内存分配和释放重复出现的情况下。这里有两个问题,一个是虚拟地址空间变得碎片化,另一个是物理4k页面上会留下未使用的空隙,随着时间的推移消耗越来越多的内存。微软通过其 .net 框架解决了4k页面问题,偶尔重新打包内存,但在重新打包期间需要“暂停” .net 应用程序。我不确定每天运行 24 小时 / 7 天的服务器应用程序如何在没有处理暂停的情况下处理此问题,除非它们偶尔分叉一个新进程来接管服务器端,然后关闭旧进程,这将使用一组新的页面刷新新进程的虚拟地址空间。

更新 - 微软Azure包括自动“负载均衡”,根据需要在核心和/或处理器之间切换,包括在.net重新打包期间。 - rcgldr

4
如果你特别担心堆碎片问题,那么你可能想要增加结构体的紧密度。这将偶尔导致不同的结构体分布在较少的不同大小的桶之间,并减少了分配时占用先前已释放的略大结构体的空间时留下的不可用间隔的可能性。
但这不太可能是你真正关心的问题。就像另一个答案指出的那样,操作系统不会立即回收释放的内存,这可能会影响进程的表面内存使用情况。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接