📄 csdn_文档中心_堆:欢乐和痛苦.htm
字号:
href="http://www.microsoft.com/china/msdn/library/techart/heap3.asp#heap3_commonproblems">“什么是常见的堆性能问题?”</A>部分)</P>
<P>在程序中,使用堆来动态分配和释放对象。在下列情况下,调用堆操作:
<OL>
<LI>事先不知道程序所需对象的数量和大小。<BR><BR>
<LI>对象太大而不适合堆栈分配程序。</LI></OL>
<P>堆使用了在运行时分配给代码和堆栈的内存之外的部分内存。下图给出了堆分配程序 的不同层。</P>
<P><IMG alt="" border=0 src="CSDN_文档中心_堆:欢乐和痛苦.files/heap3.gif"></P>
<P><B>GlobalAlloc/GlobalFree:</B>Microsoft Win32 堆调用,这些调用直接与每个进
程的默认堆进行对话。</P>
<P><B>LocalAlloc/LocalFree:</B>Win32 堆调用(为了与 Microsoft Windows NT
兼容),这些调用直接与每个进程的默认堆进行对话。</P>
<P><B>COM 的 IMalloc 分配程序(或 CoTaskMemAlloc /
CoTaskMemFree):</B>函数使用每个进程的默认堆。自动化程序使用“组件对象模型
(COM)”的分配程序,而申请的程序使用每个进程堆。</P>
<P><B>C/C++ 运行时 (CRT) 分配程序:</B>提供了 <B>malloc()</B> 和 <B>free()</B>
以及 <B>new</B> 和 <B>delete</B> 操作符。如 Microsoft Visual Basic 和 Java
等语言也提供了新的 操作符并使用垃圾收集来代替堆。CRT 创建自己的私有堆,驻留在 Win32 堆的顶部。</P>
<P>Windows NT 中,Win32 堆是 Windows NT 运行时分配程序周围的薄层。所有 API 转 发它们的请求给
NTDLL。</P>
<P>Windows NT 运行时分配程序提供 Windows NT 内的核心堆分配程序。它由具有 128 个大小从 8 到 1,024
字节的空闲列表的前端分配程序组成。后端分配程序使用虚拟内存来保留和提交页。</P>
<P>在图表的底部是“虚拟内存分配程序”,操作系统使用它来保留和提交页。所有分配 程序使用虚拟内存进行数据的存取。</P>
<P>分配和释放块不就那么简单吗?为何花费这么长时间?</P>
<H2><A name=heap3_heapimplementation></A>堆实现的注意事项</H2>
<P>传统上,操作系统和运行时库是与堆的实现共存的。在一个进程的开始,操作系统创
建一个默认堆,叫做“进程堆”。如果没有其他堆可使用,则块的分配使用“进程堆”。 语言运行时也能在进程内创建单独的堆。(例如,C
运行时创建它自己的堆。)除这些专用的堆外,应用程序或许多已载入的动态链接库 (DLL) 之一可以创建和使用单独的堆。Win32
提供一整套 API 来创建和使用私有堆。有关<A
href="http://msdn.microsoft.com/library/psdk/winbase/memman_0ulv.htm">堆函数(
英文)</A>的详尽指导,请参见 MSDN。</P>
<P>当应用程序或 DLL 创建私有堆时,这些堆存在于进程空间,并且在进程内是可访问的。从给定堆分配的数据
将在同一个堆上释放。(不能从一个堆分配而在另一个堆释放。)</P>
<P>在所有虚拟内存系统中,堆驻留在操作系统的“虚拟内存管理器”的顶部。语言运行
时堆也驻留在虚拟内存顶部。某些情况下,这些堆是操作系统堆中的层,而语言运行时堆
则通过大块的分配来执行自己的内存管理。不使用操作系统堆,而使用虚拟内存函数更利 于堆的分配和块的使用。</P>
<P>典型的堆实现由前、后端分配程序组成。前端分配程序维持固定大小块的空闲列表。
对于一次分配调用,堆尝试从前端列表找到一个自由块。如果失败,堆被迫从后端(保留
和提交虚拟内存)分配一个大块来满足请求。通用的实现有每块分配的开销,这将耗费执 行周期,也减少了可使用的存储空间。</P>
<P>Knowledge Base 文章 Q10758,“用 calloc() 和 malloc() 管理内存” (搜索文章 编号),
包含了有关这些主题的更多背景知识。另外,有关堆实现和设计的详细讨论也可在下列著 作中找到:“Dynamic Storage
Allocation: A Survey and Critical Review”,作者 Paul R. Wilson、Mark S.
Johnstone、Michael Neely 和 David Boles;“International Workshop on
Memory Management”, 作者 Kinross, Scotland, UK, 1995 年 9
月(http://www.cs.utexas.edu/users/oops/papers.html)(英文)。 </P>
<P>Windows NT 的实现(Windows NT 版本 4.0 和更新版本) 使用了 127 个大小从 8 到 1,024
字节的 8 字节对齐块空闲列表和一个“大块”列表。“大块”列表(空闲列表[0]) 保存大于 1,024
字节的块。空闲列表容纳了用双向链表链接在一起的对象。默认情况下,“进程堆”执行
收集操作。(收集是将相邻空闲块合并成一个大块的操作。)收集耗费了额外的周期,但 减少了堆块的内部碎片。</P>
<P>单一全局锁保护堆,防止多线程式的使用。(请参见“Server Performance and Scalability
Killers”中的第一个注意事项, George Reilly 所著,在 “MSDN Online Web
Workshop”上(站点:<A
href="http://msdn.microsoft.com/workshop/server/iis/tencom.asp">http://msdn.microsoft.com/workshop/server/iis/tencom.asp</A>。)单一全局锁本质上是用
来保护堆数据结构,防止跨多线程的随机存取。若堆操作太频繁,单一全局锁会对性能有 不利的影响。</P>
<H2><A name=heap3_commonproblems></A>什么是常见的堆性能问题?</H2>
<P>以下是您使用堆时会遇到的最常见问题:
<UL type=disc>
<LI><B>分配操作造成的速度减慢。</B>光分配就耗费很长时间。最可能导致运行速度
减慢原因是空闲列表没有块,所以运行时分配程序代码会耗费周期寻找较大的空闲块,或 从后端分配程序分配新块。<BR><BR>
<LI><B>释放操作造成的速度减慢。</B>释放操作耗费较多周期,主要是启用了收集操
作。收集期间,每个释放操作“查找”它的相邻块,取出它们并构造成较大块,然后再把
此较大块插入空闲列表。在查找期间,内存可能会随机碰到,从而导致高速缓存不能命中 ,性能降低。<BR><BR>
<LI><B>堆竞争造成的速度减慢。</B>当两个或多个线程同时访问数据,而且一个线程
继续进行之前必须等待另一个线程完成时就发生竞争。竞争总是导致麻烦;这也是目前多
处理器系统遇到的最大问题。当大量使用内存块的应用程序或 DLL
以多线程方式运行(或运行于多处理器系统上)时将导致速度减慢。单一锁定的使用—
常用的解决方案—意味着使用堆的所有操作是序列化的。当等待锁定时序列化会引起线程
切换上下文。可以想象交叉路口闪烁的红灯处走走停停导致的速度减慢。
<P class=tl>竞争通常会导致线程和进程的上下文切换。上下文切换的开销是很大的,但
开销更大的是数据从处理器高速缓存中丢失,以及后来线程复活时的数据重建。</P>
<LI><B>堆破坏造成的速度减慢。</B>造成堆破坏的原因是应用程序对堆块的不正确使
用。通常情形包括释放已释放的堆块或使用已释放的堆块,以及块的越界重写等明显问题
。(破坏不在本文讨论范围之内。有关内存重写和泄漏等其他细节,请参见 <A
href="http://msdn.microsoft.com/library/devprods/vs6/visualc/vccore/debughm.htm">Microsoft
Visual C++(R) 调试文档</A> 。)<BR><BR>
<LI><B>频繁的分配和重分配造成的速度减慢。</B>这是使用脚本语言时非常普遍的现
象。如字符串被反复分配,随重分配增长和释放。不要这样做,如果可能,尽量分配大字
符串和使用缓冲区。另一种方法就是尽量少用连接操作。</LI></UL>
<P>竞争是在分配和释放操作中导致速度减慢的问题。理想情况下,希望使用没有竞争和
快速分配/释放的堆。可惜,现在还没有这样的通用堆,也许将来会有。</P>
<P>在所有的服务器系统中(如 IIS、MSProxy、DatabaseStacks、网络服务器、 Exchange 和其他),
堆锁定实在是个大瓶颈。处理器数越多,竞争就越会恶化。</P>
<H2><A name=heap3_protect></A>尽量减少堆的使用</H2>
<P>现在您明白使用堆时存在的问题了,难道您不想拥有能解决这些问题的超级魔棒吗?
我可希望有。但没有魔法能使堆运行加快—因此不要期望在产品出货之前的最后一星期能
够大为改观。如果提前规划堆策略,情况将会大大好转。调整使用堆的方法,减少对堆的 操作是提高性能的良方。</P>
<P>如何减少使用堆操作?通过利用数据结构内的位置可减少堆操作的次数。请考虑下列 实例:</P><PRE><CODE>struct ObjectA {
// objectA 的数据
}
struct ObjectB {
// objectB 的数据
}
// 同时使用 objectA 和 objectB
//
// 使用指针
//
struct ObjectB {
struct ObjectA * pObjA;
// objectB 的数据
}
//
// 使用嵌入
//
struct ObjectB {
struct ObjectA pObjA;
// objectB 的数据
}
//
// 集合 – 在另一对象内使用 objectA 和 objectB
//
struct ObjectX {
struct ObjectA objA;
struct ObjectB objB;
}
</CODE></PRE>
<OL>
<LI><B>避免使用指针关联两个数据结构。</B>如果使用指针关联两个数据结构,前面 实例中的对象 A 和 B
将被分别分配和释放。这会增加额外开销—我们要避免这种做法。<BR><BR>
<LI><B>把带指针的子对象嵌入父对象。</B>当对象中有指针时,则意味着对象中有动
态元素(百分之八十)和没有引用的新位置。嵌入增加了位置从而减少了进一步分配/释 放的需求。这将提高应用程序的性能。<BR><BR>
<LI><B>合并小对象形成大对象(聚合)。</B>聚合减少分配和释放的块的数量。如果
有几个开发者,各自开发设计的不同部分,则最终会有许多小对象需要合并。集成的挑战 就是要找到正确的聚合边界。<BR><BR>
<LI><B>内联缓冲区能够满足百分之八十的需要(aka 80-20
规则)。</B>个别情况下,需要内存缓冲区来保存字符串/二进制数据,但事先不知道
总字节数。估计并内联一个大小能满足百分之八十需要的缓冲区。对剩余的百分之二十,
可以分配一个新的缓冲区和指向这个缓冲区的指针。这样,就减少分配和释放调用并增加
数据的位置空间,从根本上提高代码的性能。<BR><BR>
<LI><B>在块中分配对象(块化)。</B>块化是以组的方式一次分配多个对象的方法。 如果对列表的项连续跟踪,例如对一个
{名称,值} 对的列表,有两种选择:选择一是为每一个“名称-值”对分配一个节点;选择二是分
配一个能容纳(如五个)“名称-值”对的结构。例如,一般情况下,如果存储四对,就
可减少节点的数量,如果需要额外的空间数量,则使用附加的链表指针。
<P class=tl>块化是友好的处理器高速缓存,特别是对于 L1-高速缓存,因为它提供了 增加的位置
—不用说对于块分配,很多数据块会在同一个虚拟页中。</P>
<LI><B>正确使用 _amblksiz。</B>C 运行时 (CRT) 有它的自定义前端分配程序,该分 配程序从后端(Win32
堆)分配大小为 _amblksiz 的块。将 _amblksiz 设置为较高的值能潜在地减少对后端的调用次数。这 只对广泛使用
CRT 的程序适用。</LI></OL>
<P>使用上述技术将获得的好处会因对象类型、大小及工作量而有所不同。但总能在性能
和可升缩性方面有所收获。另一方面,代码会有点特殊,但如果经过深思熟虑,代码还是 很容易管理的。</P>
<H3>其他提高性能的技术</H3>
<P>下面是一些提高速度的技术:
<OL>
<LI><B>使用
Windows<BR></B></LI></OL></DIV></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR>
<TABLE align=center bgColor=#006699 border=0 cellPadding=0 cellSpacing=0
width=770>
<TBODY>
<TR bgColor=#006699>
<TD align=middle bgColor=#006699 id=white><FONT
color=#ffffff>对该文的评论</FONT></TD>
<TD align=middle>
<SCRIPT src="CSDN_文档中心_堆:欢乐和痛苦.files/readnum.htm"></SCRIPT>
</TD></TR></TBODY></TABLE>
<TABLE align=center bgColor=#666666 border=0 cellPadding=2 cellSpacing=1
width=770>
<TBODY>
<TR>
<TD bgColor=#cccccc colSpan=3><SPAN style="COLOR: #cccccc"><IMG height=16
hspace=1 src="CSDN_文档中心_堆:欢乐和痛苦.files/ico_pencil.gif" width=16>
</SPAN> ghj1976 <I>(2000-8-28 11:29:23)</I> </TD></TR>
<TR>
<TD bgColor=#ffffff colSpan=3
width=532><BR>http://www.microsoft.com/china/msdn/library/techart/heap3.asp#top
<BR></TD></TR></TBODY></TABLE>
<TABLE align=center bgColor=#666666 border=0 cellPadding=2 cellSpacing=1
width=770>
<TBODY>
<TR>
<TD bgColor=#cccccc colSpan=3><SPAN style="COLOR: #cccccc"><IMG height=16
hspace=1 src="CSDN_文档中心_堆:欢乐和痛苦.files/ico_pencil.gif" width=16>
</SPAN> ghj1976 <I>(2000-8-28 11:20:44)</I> </TD></TR>
<TR>
<TD bgColor=#ffffff colSpan=3
width=532><BR>http:www.microsoft.comchinamsdnlibrarytechartheap3.asptop
<BR></TD></TR></TBODY></TABLE><BR>
<DIV align=center>
<TABLE align=center bgColor=#cccccc border=0 cellPadding=2 cellSpacing=1
width=770>
<TBODY>
<TR>
<TH bgColor=#006699 id=white><FONT
color=#ffffff>我要评论</FONT></TH></TR></TBODY></TABLE></DIV>
<DIV align=center>
<TABLE border=0 width=770>
<TBODY>
<TR>
<TD>你没有登陆,无法发表评论。 请先<A
href="http://www.csdn.net/member/login.asp?from=/Develop/read_article.asp?id=471">登陆</A>
<A
href="http://www.csdn.net/expert/zc.asp">我要注册</A><BR></TD></TR></TBODY></TABLE></DIV><BR>
<HR noShade SIZE=1 width=770>
<TABLE border=0 cellPadding=0 cellSpacing=0 width=500>
<TBODY>
<TR align=middle>
<TD height=10 vAlign=bottom><A
href="http://www.csdn.net/intro/intro.asp?id=2">网站简介</A> - <A
href="http://www.csdn.net/intro/intro.asp?id=5">广告服务</A> - <A
href="http://www.csdn.net/map/map.shtm">网站地图</A> - <A
href="http://www.csdn.net/help/help.asp">帮助信息</A> - <A
href="http://www.csdn.net/intro/intro.asp?id=2">联系方式</A> - <A
href="http://www.csdn.net/english">English</A> </TD>
<TD align=middle rowSpan=3><A
href="http://www.hd315.gov.cn/beian/view.asp?bianhao=010202001032100010"><IMG
border=0 height=48 src="CSDN_文档中心_堆:欢乐和痛苦.files/biaoshi.gif"
width=40></A></TD></TR>
<TR align=middle>
<TD vAlign=top>百联美达美公司 版权所有 京ICP证020026号</TD></TR>
<TR align=middle>
<TD vAlign=top><FONT face=Verdana>Copyright © CSDN.net, Inc. All rights
reserved</FONT></TD></TR>
<TR>
<TD height=15></TD>
<TD></TD></TR></TBODY></TABLE></DIV>
<DIV></DIV><!--内容结束//--><!--结束//--></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -