zoukankan      html  css  js  c++  java
  • windows程序员进阶系列:《软件调试》之堆 (一)

                            windows程序员进阶系列:《软件调试》之堆 (一)

     

    堆是软件在运行时动态申请内存空间的主要途径。从堆上申请来的空间需要程序员自己申请和释放,且申请和释放操作必须绝对匹配。忘记释放或者多次释放可能会导致严重问题。

    与栈类似,使用堆的过程中也会由于向堆中写入超过所申请堆大小的数据,覆盖堆上的其他内容,而导致堆溢出问题。

    由于堆的特殊性,一旦堆被破坏将会导致一些严重的问题。由于如果对堆不够了解的话对于这些问题将会手足无措。本文将会详细的介绍堆的方方面面,通过以下几篇文章的讲解便可以很清晰的了解堆的结构,再遇到在堆上出现的问题将从容应对而不再是手足无措。

    由于堆的复杂性,本主题将会分五篇文章来介绍。

    第一篇文章将介绍堆的基本概念以及堆的分类。

    第二篇文章将介绍win32堆的内部结构。

    第三篇文章将介绍CRT堆的内部结构。

    第四篇文章介绍堆的调试支持。

    第五篇文章将分析一个堆破坏的实例。

     

    栈是分配局部变量和函数调用的主要场所。编译器在编译时会生成合适的代码来从栈上分配空间。因此栈又被称为“自动内存”。但是栈也有自己的不足:首先由于栈是非常小的,不适合分配特别大(动辄几十M)的内存区。再者栈会随着函数的调用和返回不断的创建和销毁。因此只适用于局部变量的分配,不适合分配具有长生命周期的变量。

    堆克服了以上栈的缺点。应用程序通过HeapAlloc mallocnew分配的空间均来自于堆。而它们均是通过堆管理器从堆中分配空间的。堆管理器用于管理堆,对外提供堆分配和释放的接口。

     

    windows操作系统的内核态有一个管理内存空间的内存管理器。堆管理器管理的空间是通过内存管理器获得的。由于应用程序申请空间的操作可能是分散且零散的,为了避免频繁的向内存管理器申请空间,堆管理器会一次性的向内存管理器批发大量的内存空间,然后零散的分配给应用程序。

    有一个形象的比喻:把内存管理器比作内存工厂,对外不接受小批量的订货。而堆管理器是该工厂的代理商,零售给应用程序。

     

    Windows操作系统的堆管理器被称为win32堆管理器,它管理的堆被称为win32堆。

    为了支持C/C++malloc new等内存分配函数。编译器的C运行库(CRT)在程序初始化时会创建一个专门的堆供这些函数使用,这个专门的堆被称为CRT堆。

    CRT堆有三种工作方式:SBH模式、旧SBH模式和系统模式。

    前两种模式时,CRT堆管理器会直接从内存管理器批发内存,然后分割成小块的堆块供应用程序使用。

    第三种模式CRT堆管理器会将对堆块的分配请求转发给win32堆管理器。因此处于系统模式的CRT堆仅仅是堆win32堆的简单封装。

    除了上面介绍的堆,应用程序也可以实现自己的堆管理器。直接向内存管理器批发内存,供本应用程序内部使用。例如:应用程序通过向内存管理器申请空间,实现的内存池就可以看做是一种堆管理器。

            

    如图所示,应用程序可以从内存管理器、win32堆、CRT堆申请空间。

    但三者有区别:

    通过内存管理器申请时是调用VirtualAlloc申请的。申请大小是以页面大小(win32下为4KB)为单位。

    通过WIN32堆管理器申请时是调用HeapAlloc申请的,但当申请的空间超过一定阈值时,WIN32堆管理器并不会从自己的堆中分配,而是重新向内存管理器申请,然后转交给应用程序。

    通过CRT堆申请空间是调用new mallocC/C++函数来实现的。同样当申请的空间超过一定阈值时,会从上一级直接申请并转交给应用程序。根据工作模式的不同,上一级可以是WIN32堆,也可以是内存管理器。

     

    内存管理类似于内存工厂,用于生产内存并向外批量出货。

    Win32堆是中间商,直接从内存工厂进货,提供批发和零售服务。

    CRT堆是更下一级的中间商,可以从内存工厂和中间商进货,对外提供零售服务。

    应用程序是消费者,可以从以上三个角色购买内存。但是只有当购买量很大时才能从内存工厂购买。

     

                                                             如有纰漏,请指正,谢谢!

                                                                  2013.10.12于浙江杭州

  • 相关阅读:
    RabbitMQ第一次不能正常读取第二次正常的问题
    ng跳转映射,被阿里云的云盾拦截,提示备案问题分析
    Java读取excel表,getPhysicalNumberOfCells()和getLastCellNum区别
    IDEA同步上传lua代码,方便开发。
    redis安装
    面试感悟----一名3年工作经验的程序员应该具备的技能
    SQL Server的case when用法
    SQL Server常用函数使用方法(学习)
    Openresty编写Lua代码一例
    Nginx的配置文件nginx.conf解析
  • 原文地址:https://www.cnblogs.com/keanuyaoo/p/3365998.html
Copyright © 2011-2022 走看看