扩展阅读
  • Windows下php 5.3.5和apache2安装配置及测试
  • 大家推荐一个windows下的java开发平台。工具本身是为windows优化的。jbuilder太慢。
  • ​Windows Server 2016提供Docker原生运行的企业级支持
  • windows下写的代码 gb2312 如何转成 LINUX和WINDOWS都可正常显示的代码
  • windows堆栈溢出利用的七种方式
  • 用linux(服)windows(客)传输文件,windows端可以,linux端不可以,怎么回事???
  • windows 7下打开或者关闭telnet客户端/服务端命令方法操作步骤
  • linux环境下安装windows打印机问题。。没有驱动,能否在无界面的linux上安装windows虚拟机??
  • windows下c/c++读写锁实现原理及代码参考
  • windows下用wincvs弄下来的emacs源码全是windows格式的,晕!!!
  • Windows 8.1中文英文预览版系统下载及功能改进
  • 请指点: 在windows下能否通过程序来获取linux下的用户列表,甚至通过自己写的windows程序界面增加修改linux的用户
  • tar.xz如何解压:linux和windows下tar.xz解压命令介绍
  • 如何修改启动菜单的启动顺序(linux,windows),我想让系统默认启动到windows.谢谢!!
  • MTU介绍以及在windows和linux下怎么设置MTU值
  • 急需windows server 2003系统c:windowssystem32文件夹下winlogon.exe、licdll.dll两个文件,在线等待!
  • windows7操作系统介绍及各种使用技巧总结
  • 多操作系统(windows98windows2000linux)怎么按装最合适???
  • Windows下Docker应用部署相关问题详解
  • asp只能在windows上运行吗,jsp可以在windows也可以在unix上运行吗
  • Windows 7 32位下 MongoDB安装步骤
  • 红旗Linux在局域网上能访问Windows的网上邻居上的windows98机器中的文件系统吗?
  •  
    当前位置:  编程语言>c/c++

    Windows C/ C++堆相关问题及解决思路

     
        发布时间:2013-9-3  


        本文导语: 堆及其基本概念在程序中,使用堆来动态分配和释放对象。在下列情况下,调用堆操作:事先不知道程序所需对象的数量和大小。对象太大而不适合堆栈分配程序。堆使用了在运行时分配给代码和堆栈的内存之外的部分内存。分...

    及其基本概念

    程序中,使用堆来动态分配和释放对象。在下列情况下,调用堆操作:

    事先不知道程序所需对象的数量和大小。

    对象太大而不适合堆栈分配程序。

    堆使用了在运行时分配给代码和堆栈的内存之外的部分内存。

    分配堆的具体调用方法

    (1)GlobalAlloc/GlobalFree:Microsoft Win32 堆调用,这些调用直接与每个进程的默认堆进行对话。

    (2)LocalAlloc/LocalFree:Win32 堆调用(为了与 Microsoft Windows NT 兼容),这些调用直接与每个进程的默认堆进行对话。

    (3)COM 的 IMalloc 分配程序(或 CoTaskMemAlloc / CoTaskMemFree):函数使用每个进程的默认堆。自动化程序使用“组件对象模型 (COM)”的分配程序,而申请的程序使用每个进程堆。

    (4)C/C ++ 运行时 (CRT) 分配程序:提供了 malloc() 和 free() 以及 newdelete 操作符。如 Microsoft Visual BasicJava语言也提供了新的操作符并使用垃圾收集来代替堆。CRT 创建自己的私有堆,驻留在 Win32 堆的顶部。

    (5)利用 heapalloc 方法或 c/c++ 运行时中的 malloc 或 new 来进行堆内存分配

    (6)利用 VirtualAlloc 方法从系统中直接分配内存

    (7)内核通过 CreateFile, CreateEvent, or CreateThreadKernel32 APIs ,来代表应用程序进行处理

    (8)利用 User32 和 Gdi32 APIs 来处理 GDI 和 USER

    预防Windows应用程序中的内存泄露

       内存泄露是指,当应用程序无需再被使用的时候,应用程序没有正常的释放内存而引起的一类错误。随着时间的累积,内存泄露将会影响个别应用程序以及系统的性能。一个严重的内存泄露可能会引起由于过度页面调度( excessive paging )而引起的不可接受的长时间的等待。最终,应用程序以及操作系统其他部分将会运行失败。

    Windows 将会在线程中终止应用程序并释放所有分配给该应用程序的内存空间,短时间运行的应用程序不会对整个系统产生巨大的影响。然而,长时间运行的线程的泄露——例如,服务甚至是浏览器插件——就会对系统的可靠性产生很大的影响,还有可能会强制用户重启 Windows 。应用程序可以以多种方式分配内存。不论哪一种分配方式,如果不在使用后对内存进行释放,都可能引起内存泄露。预防的具体方法如下:

    (1)在 C++ 代码中对堆分配以及 Win32 资源——例如,内核 HANDLEs ——使用智能指针( smart pointers )。 C++ 标准库提供了 auto_ptr 类来进行堆分配。对于其他的分配类型,那你需要写自己的类。 ATL 库提供了丰富的类来对堆对象和内核处理( kernel handles )进行自动资源管理。

    (2)使用编译器内在的特性——例如 _com_ptr_t ——来将您的 COM 接口指针( COM interface pointers )封装到“智能指针( smart pointers )”并帮助进行引用计数reference counting )。对于其他的 COM 数据类型,也有一些相似的 : _bstr_t and _variant_yt

    (3)管理您的 .net 代码中的对于内存的不正常使用。托管代码并非不会受到内存泄露的影响。垃圾回收器 (gc) 不会在一个对象的引用数还没有到 0 的时候,对该对象进行释放。请查看 " 追踪托管内存的泄露 " 了解如何找出 gc 泄露。

    (4)注意客户端代码的泄露模式。 COMpany 对象间的循环引用脚本引擎——例如, JScript ——可能会引起 web 应用程序的大型泄露。文章 " 理解并解决 Internet Explorer 泄露模式 " 中包含关于这类泄露的更多信息。您可以在您的代码中使用 JavaScriptn 内存泄露探测器。 Internet Explorer 8, 将随 Windows 7 一同发布 , 缓解大多数关于这类的问题,以前版本的浏览器对这些问题还会比较敏感。

    (5)避免在一个法中使用多个出口。方法内的指派给变量的内存分配,应该在方法结束末尾的,特定的代码段中进行释放。

    (6)不要在没有释放所有本地变量的方法中使用 exception 。如果您使用本机的 exception ,请将您所有的内存分配在 __ finally 中进行释放。如果您使用 C++ exceptions ,您所有的堆以及处理分配都需要封装为智能指针( smart pointers )。

    (7)如果没有调用 PropVariantClear 方法,请不要丢弃或重新预置 PROPVARIANT 对象。

    堆实现的注意事项

      传统上,操作系统和运行时库是与堆的实现共存的。在一个进程的开始,操作系统创建一个默认堆,叫做“进程堆”。如果没有其他堆可使用,则块的分配使用“进程堆”。语言运行时也能在进程内创建单独的堆。(例如,C 运行时创建它自己的堆。)除这些专用的堆外,应用程序或许多已载入的动态链接库 (DLL) 之 一可以创建和使用单独的堆。Win32提供一整套API来创建和使用私有堆。有关堆函数(英文)的详尽指导,请参见 MSDN

       当应用程序或 DLL 创建私有堆时,这些堆存在于进程空间,并且在进程内是可访问的。从给定堆分配的数据将在同一个堆上释放。(不能从一个堆分配而在另一个堆释放。)

       在所有虚拟内存系统中,堆驻留在操作系统的“虚拟内存管理器”的顶部。语言运行时堆也驻留在虚拟内存顶部。某些情况下,这些堆是操作系统堆中的层,而语言运行时堆则通过大块的分配来执行自己的内存管理。不使用操作系统堆,而使用虚拟内存函数更利于堆的分配和块的使用。

       典型的堆实现由前、后端分配程序组成。前端分配程序维持固定大小块的空闲列表。对于一次分配调用,堆尝试从前端列表找到一个自由块。如果失败,堆被迫从后端 (保留和提交虚拟内存)分配一个大块来满足请求。通用的实现有每块分配的开销,这将耗费执行周期,也减少了可使用的存储空间。

      Windows NT 的实现(Windows NT 版本 4.0 和更新版本) 使用了 127 个大小从 8 到 1,024 字节的 8 字节对齐块空闲列表和一个“大 块”列表。“大块”列表(空闲列表[0]) 保存大于 1,024 字节的块。空闲列表容纳了用双向链表链接在一起的对象。默认情况下,“进程堆”执行收集操作。(收集是将相邻空闲块合并成一个大块的操作。)收集耗费了额外的周期,但减少了堆块的内部碎片。

      单一全局保护堆,防止多线程式的使用。单一全局锁本质上是用来保护堆数据结构,防止跨多线程的随机存取。若堆操作太频繁,单一全局锁会对性能有不利的影响。

    常见的堆性能问题

    以下是您使用堆时会遇到的最常见问题

     (1)分配操作造成的速度减慢。光分配就耗费很长时间。最可能导致运行速度减慢原因是空闲列表没有块,所以运行时分配程序代码会耗费周期寻找较大的空闲块,或从后端分配程序分配新块。

     (2)释放操作造成的速度减慢。释放操作耗费较多周期,主要是启用了收集操作。收集期间,每个释放操作“查找”它的相邻块,取出它们并构造成较大块,然后再把此较大块插入空闲列表。在查找期间,内存可能会随机碰到,从而导致高速缓存不能命中,性能降低。

     (3)堆竞争造成的速度减慢。当两个或多个线程同时访问数据,而且一个线程继续进行之前必须等待另一个线程完成时就发生竞争。竞争总是导致麻烦;这也是目前多处理 器系统遇到的最大问题。当大量使用内存块的应用程序或 DLL 以多线程方式运行(或运行于多处理器系统上)时将导致速度减慢。单一锁定的使用—常用的解 决方案—意味着使用堆的所有操作是序列化的。当等待锁定时序列化会引起线程切换上下文。可以想象交叉路口闪烁的红灯处走走停停导致的速度减慢。竞争通常会导致线程和进程的上下文切换。上下文切换的开销是很大的,但开销更大的是数据从处理器高速缓存中丢失,以及后来线程复活时的数据重建。

    堆破坏造成的速度减慢。造成堆破坏的原因是应用程序对堆块的不正确使用。通常情形包括释放已释放的堆块或使用已释放的堆块,以及块的越界重写等明显问题。

     (4)频繁的分配和重分配造成的速度减慢。这是使用脚本语言时非常普遍的现象。如字符串被反复分配,随重分配增长和释放。不要这样做,如果可能,尽量分配大字符串和使用缓冲区。另一种方法就是尽量少用连接操作。竞争是在分配和释放操作中导致速度减慢的问题。理想情况下,希望使用没有竞争和快速分配/释放的堆。可惜,现在还没有这样的通用堆,也许将来会有。

     (5)在所有的服务器系统中(如 IIS、MSProxy、DatabaseStacks、网络服务器、 Exchange 和其他), 堆锁定实在是个大瓶颈。处理器数越多,竞争就越会恶化。

    尽量减少堆的使用

     现在您明白使用堆时存在的问题了,难道您不想拥有能解决这些问题的超级魔棒吗?我可希望有。但没有魔法能使堆运行加快—因此不要期望在产品出货之前的最后一星期能够大为改观。如果提前规划堆策略,情况将会大大好转。调整使用堆的方法,减少对堆的操作是提高性能的良方。

    如何减少使用堆操作?通过利用数据结构内的位置可减少堆操作的次数。请考虑下列实例

    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;
    }

      避免使用指针关联两个数据结构。如果使用指针关联两个数据结构,前面实例中的对象 A 和 B 将被分别分配和释放。这会增加额外开销—我们要避免这种做法。

      把带指针的子对象嵌入父对象。当对象中有指针时,则意味着对象中有动态元素(百分之八十)和没有引用的新位置。嵌入增加了位置从而减少了进一步分配/释放的需求。这将提高应用程序的性能。

      合并小对象形成大对象(聚合)。聚合减少分配和释放的块的数量。如果有几个开发者,各自开发设计的不同部分,则最终会有许多小对象需要合并。集成的挑战就是要找到正确的聚合边界。

      内联缓冲区能够满足百分之八十的需要(aka 80-20 规则)。个别情况下,需要内存缓冲区来保存字符串/二进制数据,但事先不知道总字节数。估计并内 联一个大小能满足百分之八十需要的缓冲区。对剩余的百分之二十,可以分配一个新的缓冲区和指向这个缓冲区的指针。这样,就减少分配和释放调用并增加数据的 位置空间,从根本上提高代码的性能。

     在块中分配对象(块化)。块化是以组的方式一次分配多个对象的方法。如果对列表的项连续跟踪, 例如对一个 {名称,值} 对的列表,有两种选择:选择一是为每一个“名称-值”对分配一个节点;选择二是分配一个能容纳(如五个)“名称-值”对的结 构。例如,一般情况下,如果存储四对,就可减少节点的数量,如果需要额外的空间数量,则使用附加的链表指针。

     块化是友好的处理器高速缓存,特别是对于 L1-高速缓存,因为它提供了增加的位置 —不用说对于块分配,很多数据块会在同一个虚拟页中。

     正确使用 _amblksiz。C 运行时 (CRT) 有它的自定义前端分配程序,该分配程序从后端(Win32 堆)分配大小为 _amblksiz 的块。将 _amblksiz 设置为较高的值能潜在地减少对后端的调用次数。这只对广泛使用 CRT 的程序适用。

     使用上述技术将获得的好处会因对象类型、大小及工作量而有所不同。但总能在性能和可升缩性方面有所收获。另一方面,代码会有点特殊,但如果经过深思熟虑,代码还是很容易管理的。

    • 本站(WWW.)旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。
      本站(WWW.)站内文章除注明原创外,均为转载,整理或搜集自网络.欢迎任何形式的转载,转载请注明出处.
      转载请注明:文章转载自:[169IT-IT技术资讯]
      本文标题:Windows C/ C++堆相关问题及解决思路
    相关文章推荐:
  • java命名空间java.awt.event类keyevent的类成员方法: vk_windows定义及介绍
  • 怎么在Linux下改windows系统文件啊,我把windows的BOOT.INI改了,windows启动不了
  • WinDows8最新版文件夹加密
  • x-windows如何安装在linux(rdehat9)上面呢,是不是x-windows也分windows和linux版本的吗?
  • 修改Windows硬盘分区名称
  • linux和windows串口问题!?linux向windows端发送,第一次write正常,继续write,windows接收到的就变成乱码了,这是什么原因??????
  • windows10玩游戏怎么样?唯一支持DirectX 12的windows
  • 装了Linux和Windows,怎样默认进入Windows
  • windows/windows 7/windows 8 下打开查看、修改及保存超大(GB级)文本文件及其它类型文件的工具-PilotEdit
  • Linux与windows共存时,如何将Windows设置为默认启动系统?
  • Docker宣布支持Windows 10和Azure Windows Server
  • linux 、 unix给windows传送文件windows
  • win7/Windows7系统下载地址搜集整理
  • 怎样是编好的java application在windows上像windows应用程序一样直接运行
  • Windows7自带防火墙设置:启动,关闭及高级设置
  • windows 和linux双系统,重装windows后,无法启动linux?
  • IE11设置IE兼容性视图及提升Windows 8.1中IE11兼容性的相关设置
  • 如何将linux的一台机器加入windows 2000的域?并且通过一windows的机器上网?
  • Windows优化大师最新版 V7.99 Build 12.604发布
  • 为什么在安装了WINDOWS和LINUX的电脑上,重装WINDOWS会破坏MBR?
  • Windows7 常用使用技巧
  • Linux + Windows2000 双启动,Windows2000起不来了,说是文件被破坏,进来看看……


  • 站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3