以文本方式查看主题 - 昂捷论坛 (http://enjoyit.com.cn/bbs/index.asp) -- □-技术研讨会 (http://enjoyit.com.cn/bbs/list.asp?boardid=36) ---- 对于堆内存分配的专业说明(摘自MSDN) (http://enjoyit.com.cn/bbs/dispbbs.asp?boardid=36&id=6621) |
-- 作者:飞絮 -- 发布时间:2009/11/24 17:29:10 -- 对于堆内存分配的专业说明(摘自MSDN)
Murali R. Krishnan 1999 年 2 月 摘要:讨论常见的堆性能问题以及如何避免这些问题。(共 9 页打印页) 介绍您是在无忧无虑地使用动态分配的 C/C++ 对象吗?您广泛地使用自动化在模块之间相互通讯吗?您的程序可能因为堆分配而变慢吗?以上这些情况不只是您一个人遇到的。几乎所有的项目迟早都会遇到堆问题。一般的倾向是说:"是堆慢,而我的代码确实是好代码。"这不完全正确。本文帮助您更多地了解堆、堆的用法以及可能产生的问题。 什么是堆?(如果您已经知道什么是堆,则可以向前跳转到"常见的堆性能问题有哪些?"一节) 堆用于动态分配和释放程序所使用的对象。在以下情况中调用堆操作:
堆使用运行期间分配给代码和堆栈以外的部分内存。下图显示堆分配器的不同层。 GlobalAlloc/GlobalFree:直接与每个进程的默认堆通讯的 Microsoft Win32 堆调用。 LocalAlloc/LocalFree:直接与每个进程的默认堆通讯的 Win32 堆调用(用于与 Microsoft Windows NT 的兼容性)。 COM 的 IMalloc 分配器(或 CoTaskMemAlloc / CoTaskMemFree):函数使用默认的每个进程堆。自动化使用组件对象模型 (COM) 的分配器,而请求使用每进程堆。 C/C++ 运行时 (CRT) 分配器:提供 malloc() 和 free() 以及 new 和 delete 运算符。编程语言如 Microsoft Visual Basic 和 Java 还提供新运算符,它们使用的是垃圾回收而不是堆。CRT 创建自己的驻留在 Win32 堆之上的专用堆。 在 Windows NT 中,Win32 堆是围绕 Windows NT 运行时分配器的一个薄层。所有的 API 都将它们的请求转发到 NTDLL。 在 Windows NT 中,Windows NT 运行时分配器提供了该核心堆分配器。它包含一个前端分配器,该分配器具有 128 个大小从 8 到 1,024 字节不等的自由列表。后端分配器使用虚拟内存保留和提交页面。 图表的底部是虚拟内存分配器,它保留和提交操作系统使用的页面。所有的分配器都使用虚拟内存设备访问数据。 分配和释放块不是很简单吗?为什么这要花费很长的时间? 有关堆实现的说明传统上,操作系统和运行时库随附了堆实现。当进程开始时,操作系统创建称为进程堆的默认堆。如果没有使用其他堆,则使用进程堆分配块。语言运行时库也可在一个进程内创建单独的堆。(例如,C 运行时库创建自己的堆。)除这些专用堆外,应用程序或许多加载的动态链接库 (DLL) 之一也可以创建并使用单独的堆。Win32 提供了一组丰富的 API 用于创建和使用专用堆。有关堆函数的优秀教程,请参阅 MSDN 平台 SDK 节点。 当应用程序或 DLL 创建专用堆时,这些堆驻留于进程空间中并且在进程范围内是可访问的。某一给定堆分配的任何数据应为同一堆所释放。(从一个堆分配并释放给另一个堆没有意义。) 在所有虚拟内存系统中,堆位于操作系统的虚拟内存管理器之上。语言运行时堆也驻留在虚拟内存之上。某些情况下,这些堆在操作系统堆的上层,但语言运行时堆通过分配大的块来执行自己的内存管理。绕开操作系统堆来使用虚拟内存函数可使堆更好地分配和使用块。 典型的堆实现由前端分配器和后端分配器组成。前端分配器维护固定大小块的自由列表。当堆收到分配调用后,它尝试从前端列表中查找自由块。如果此操作失败,则堆将被迫从后端(保留和提交虚拟内存)分配一个大块来满足请求。通常的实现具有每个块分配的开销,这花费了执行周期,也减少了可用存储区。 知识库文章 Q10758"Managing Memory with calloc() and malloc()"(按文章 ID 号搜索)包含有关这些主题的更多背景知识。另外,有关堆实现和设计的详细讨论,请参阅 Paul R. Wilson、Mark S. Johnstone、Michael Neely 和 David Boles 所著的"Dynamic Storage Allocation: A Survey and Critical Review"。"International Workshop on Memory Management",Kinross,Scotland,UK,1995 年 9 月 (http://www.cs.utexas.edu/users/oops/papers.html)。 Windows NT 的实现(Windows NT 4.0 版及更高版本)使用 127 个从 8 到 1,024 字节不等的 8 字节对齐块的自由列表和 1 个混合列表。混合列表(自由列表[0])包含大小超过 1,024 字节的块。自由列表包含在双向链接表中链接在一起的对象。默认情况下,进程堆执行合并操作。(合并操作是组合相邻的自由块以生成更大的块的操作。)合并操作花费了额外的周期,但减少了堆块的内部碎片。 单个全局锁可防止多线程同时使用堆。(请参阅 George Reilly 写的服务器性能和可伸缩性杀手锏的第一条戒律。)此锁主要用于保护堆数据结构不受多线程的任意访问。当堆操作过于频繁时,此锁会对性能造成负面影响。 常见的堆性能问题有哪些?以下是在使用堆时会遇到的最常见问题:
在分配及释放操作中,争用是使速度降低的原因。理想情况下,我们希望有一个没有争用且快速分配/释放的堆。哎,这样的通用用途堆尚不存在,尽管在将来某个时候也许会出现。 在所有的服务器系统(如 IIS、MSProxy、DatabaseStacks、网络服务器、Exchange 等等)中,堆锁都是一个"大"瓶颈。处理器的数目越多,争用越厉害。 保护自己不受堆的影响既然您知道了关于堆的一些问题,难道不想要一根解决这些问题的魔棒吗?我希望有一根魔棒。但是没有魔法使堆运行得更快,因此不要期望在发行产品的前一个星期内使速度加快。请尽早计划您的堆策略,这样会好得多。改变使用堆的方式并减少堆操作数是提高性能的可靠策略。 如何减少堆操作的使用呢?可以通过在数据结构内使用位置来减少堆操作数。考虑下面的示例: struct ObjectA {
通过使用上述技巧获得的节省因对象类型、大小和工作负荷而异。但总是能获得性能和可缩放性方面的好处。退一步说,代码会有点专用,但是如果认真思考,代码还是可以易于管理的。 更多性能提升以下是一些提升速度的更多技巧:
摘要堆实现趋于对所有平台保持通用,因此具有巨大的系统开销。每个人的代码都有特定的要求,但是设计可以适应本文所讨论的原则以减少堆交互。
Murali Krishnan 是 Internet Information Server (IIS) 团队的首席软件设计工程师。他从 1.0 版就开始研究 IIS,并成功地将 IIS 从 1.0 版一直升级到 4.0。Murali 组织和领导了 IIS 性能小组三年 (1995-1998),并从第一天起就开始改变 IIS 的性能。他在印第安纳州的 Anna 大学获得计算机科学学士学位,并在 Wisconsin-Madison 大学获得计算机科学硕士学位。工作之余,他喜欢读书、打排球和在家烹饪。 <!--Footer Start--> |
-- 作者:ccav123 -- 发布时间:2012/1/5 12:26:43 -- win2k对堆的管理基本上已经公开了,虽然是未文档化的。 基本上现在已经对以前的堆管理做了很大的优化,尤其是关于堆的安全方面。 对堆的使用在任何程序里是必须的,堆的申请和释放必须由程序员来维护。 |