`
bbls
  • 浏览: 63222 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类

内存堆栈

阅读更多

对内存进 行操作的第三个机制是使用堆栈。堆栈可以用来分配许多较小的数据块。例如,若要对链接表和链接树进行管理,最好的方法是使用堆栈,堆栈的优点是,可以不考 虑分配粒度和页面边界之类的问题,集中精力处理手头的任务。堆栈的缺点是,分配和释放内存块的速度比其他机制要慢,并且无法直接控制物理存储器的提交和回 收。
从内部来讲,堆栈是保留的地址空间的一个区域。开始时,保留区域中的大多数页面没有被 提交物理存储器。当从堆栈中进行越来越多的内存分配时,堆栈管理器将把更多的物理存储器提交给堆栈。物理存储器总是从系统的页文件中分配的,当释放堆栈中 的内存块时,堆栈管理器将收回这些物理存储器。

线程的堆栈:

每当创建一个线程时,系统就会为线程的堆栈(每个线程有它自己的堆栈) 保留一个堆栈空间区域,并将一些物理存储器提交给这个已保留的区域。按照默认设置,系统保留1 MB的地址空间并提交两个页面的内存。但是,这些默认值是可以修改的,方法是在你链接应用程序时设定M i c r o s o f t的链接程序的/ S TA C K选项:

/STACK:reserve[,commit]









当创建一个线程的堆栈时,系统将会保留一个链接程序的/ S TA C K开关指明的地址空间区域。
进程的默认堆栈

当进程初始化时,系统在进程的地址空间中创建一个堆栈。该堆栈称为进程的默认堆栈。按照默认设置, 该堆栈的地址空间区域的大小是1 MB。但是,系统可以扩大进程的默认堆栈,使它大于其默认值。当创建应用程序时,可以使用/ H E A P链接开关,改变堆栈的1 M B默认区域大小。由于D L L没有与其相关的堆栈,所以当链接D L L时,不应该使用/ H E A P链接开关。/ H E A P链接开关的句法如下:

/HEAP:reserve[,commit]









许多Wi n d o w s函数要求进程使用其默认堆栈。特别是widows提供的API。对默认堆栈的访问是顺序进行的。换句话说,系统必须保证在规定的时间内,每次只有一个线 程能够分配和释放默认堆栈中的内存块。如果两个线程试图同时分配默认堆栈中的内存块,那么只有一个线程能够分配内存块,另一个线程必须等待第一个线程的内 存块分配之后,才能分配它的内存块。一旦第一个线程的内存块分配完,堆栈函数将允许第二个线程分配内存块。这种顺序访问方法对速度有一定的影响。如果你的 应用程序只有一个线程,并且你想要以最快的速度访问堆栈,那么应该创建你自己的独立的堆栈,不要使用进程的默认堆栈。

单个进程可以同时拥有若干个堆栈。这些堆栈可以在进程的寿命期中创建和撤消。但是,默认堆栈是在进 程开始执行之前创建的,并且在进程终止运行时自动被撤消。不能撤消进程的默认堆栈。每个堆栈均用它自己的堆栈句柄来标识,用于分配和释放堆栈中的内存块的 所有堆栈函数都需要这个堆栈句柄作为其参数。

可以通过调用G e t P r o c e s s H e a p函数获取你的进程默认堆栈的句柄:

HANDLE GetProcessHeap();









为什么要创建辅助堆栈

除了进程的默认堆栈外,可以在进程的地址空间中创建一些辅助堆栈。由于下列原因,你可能想要在自己 的应用程序中创建一些辅助堆栈:

• 保护组件。

• 更加有效地进行内存管理。

• 进行本地访问。

• 减少线程同步的开销。

• 迅速释放。

保护组件
通过创建多个独立的堆栈,是数据隔离,且相互独立的操作。
更有效的内存管理

通过在堆栈中分配同样大小的对象,就可以更加有效地管理堆栈。就是把大小相同的对象放在一个堆栈中 进行分配。
进行本地访问

每当系统必须在R A M与系统的页文件之间进行R A M页面的交换时,系统的运行性能就会受到很大的影响。如果经常访问局限于一个小范围地址的内存,那么系统就不太可能需要在R A M与磁盘之间进行页面的交换。

所以,在设计应用程序的时候,如果有些数据将被同时访问,那么最好把它们分配在互相靠近的位置上。
减少线程同步的开销

正如下面就要介绍的那样,按照默认设置,堆栈是顺序运行的,这样,如果多个线程试图同时访问堆栈, 就不会使数据受到破坏。但是,堆栈函数必须执行额外的代码,以保证堆栈对线程的安全性。如果要进行大量的堆栈分配操作,那么执行这些额外的代码会增加很大 的负担,从而降低你的应用程序的运行性能。当你创建一个新堆栈时,可以告诉系统,只有一个线程将访问该堆栈,因此额外的代码将不执行。(就是用多个堆栈来 减少同步的性能消耗)
迅速释放堆栈

最后要说明的是,将专用堆栈用于某些数据结构后,就可以释放整个堆栈,而不必显式释放堆栈中的每个 内存块。例如,当Windows Explorer遍历硬盘驱动器的目录层次结构时,它必须在内存中建立一个树状结构。如果你告诉Windows Explorer刷新它的显示器,它只需要撤消包含这个树状结构的堆栈并且重新运行即可(当然,假定它将专用堆栈用于存放目录树信息)。对于许多应用程序 来说,这是非常方便的,并且它们也能更快地运行。

如何创建辅助堆栈

你可以在进程中创建辅助堆栈,方法是让线程调用H e a p C r e a t e函数:

HANDLE HeapCreate(
   DWORD fdwOptions,
   SIZE_T dwInitialSize,
   SIZE_T dwMaximumSize);









当试图从 堆栈分配一个内存块时, H e a p A l l o c函数(下面将要介绍)必须执行下列操作:

1) 遍历分配的和释放的内存块的链接表。

2) 寻找一个空闲内存块的地址。

3) 通过将空闲内存块标记为“已分配”分配新内存块。

4) 将新内存块添加给内存块链接表。

从堆栈中分配内存块

若要从堆栈中分配内存块,只需要调用H e a p A l l o c函数:

PVOID HeapAlloc(
   HANDLE hHeap,
   DWORD fdwFlags,
   SIZE_T dwBytes);









改变内存块的大小

常常需要改变内存块的大小。有些应用程序开始时分配的内存块比较大,然 后,当所有数据放入内存块后,再缩小内存块的大小。有些应用程序开始时分配的内存块比较小,后来需要将更多的数据拷贝到内存块中去时,再设法扩大它的大 小。如果要改变内存块的大小,可以调用H e a p R e A l l o c函数:

PVOID HeapReAlloc(
   HANDLE hHeap,
   DWORD fdwFlags,
   PVOID pvMem,
   SIZE_T dwBytes);









了解内存块的大小

当内存块分配后,可以调用H e a p S i z e函数来检索内存块的实际大小:

SIZE_T HeapSize(
   HANDLE hHeap,
   DWORD fdwFlags,
   LPCVOID pvMem);









释放内存块

当不再需要内存块时,可以调用H e a p F r e e函数将它释放:

BOOL HeapFree(
   HANDLE hHeap,
   DWORD fdwFlags,
   PVOID pvMem);









撤消堆栈

如果应用程序不再需要它创建的堆栈,可以通过调用H e a p D e s t r o y函数将它撤消:

BOOL HeapDestroy(HANDLE hHeap);









调用H e a p D e s t r o y函数可以释放堆栈中包含的所有内存块,也可以将堆栈占用的物理存储器和保留的地址空间区域重新返回给系统。如果该函数运行成功, H e a p D e s t r o y返回T R U E。如果在进程终止运行之前没有显式撤消堆栈,那么系统将为你将它撤消。但是,只有当进程终止运行时,堆栈才能被撤消。如果线程创建了一个堆栈,当线程终 止运行时,该堆栈将不会被撤消。

在进程完全终止运行之前,系统不允许进程的默认堆栈被撤消。如果将进程 的默认堆栈的句柄传递给H e a p D e s t r o y函数,系统将忽略对该函数的调用。

由于进程的地址空间中可以存在多个堆栈,因此可以使用G e t P r o c e s s H e a p s函数来获取现有堆栈的句柄:

DWORD GetProcessHeaps(
   DWORD dwNumHeaps,
   PHANDLE pHeaps);









若要调用G e t P r o c e s s H e a p s函数,必须首先分配一个H A N D L E数组,然后调用下面的函数:

HANDLE hHeaps[25];
DWORD dwHeaps = GetProcessHeaps(25, hHeaps);
if(dwHeaps > 5) 
{
   //More heaps are in this process than we expected.
} 
else
{
   //hHeaps[0] through hHeap[dwHeaps - 1]
   //identify the existing heaps.
}









注意,当该函数返回时,你的进程的默认堆栈的句柄也包含在堆栈句柄的数 组中。

H e a p Va l i d a t e函数用于验证堆栈的完整性:

BOOL HeapValidate(
   HANDLE hHeap,
   DWORD fdwFlags,
   LPCVOID pvMem);









调用该函数时,通常要传递一个堆栈句柄,一个值为0的标志(唯一的另一 个合法标志是H E A P _ N O _ S E R I A L I Z E),并且为p v M e m传递N U L L。然后,该函数将遍历堆栈中的内存块以确保所有内存块都完好无损。为了使该函数运行得更快,可以为参数p v M e m传递一个特定的内存块的地址。这样做可使该函数只检查单个内存块的有效性。

若要合并地址中的空闲内存块并收回不包含已经分配的地址内存块的存储器 页面,可以调用下面的函数:

UINT HeapCompact(
   HANDLE hHeap,
   DWORD fdwFlags);










通常情况下,可以为参数f d w F l a g s传递0,但是也可以传递H E A P _ N O _ S E R I A L I Z E。

下面两个函数H e a p L o c k和H e a p U n l o c k是结合在一起使用的:

<span

分享到:
评论

相关推荐

    内存堆栈分析工具MAT 64bit软件(Memory Analysis Tool)

    内存堆栈分析是Java应用程序性能优化的关键环节,尤其是在长期运行的服务中,内存泄漏可能导致系统资源耗尽,甚至引发服务崩溃。MAT(Memory Analyzer Tool)是一款强大的64位内存分析工具,由Eclipse基金会开发,专...

    易语言申请进程堆栈内存

    进程的堆栈内存管理是程序员必须理解和掌握的关键技能,特别是在低级编程或者系统级编程中。易语言,一种面向对象、简单易学的中文编程语言,也提供了对进程堆栈内存的操作支持。本文将详细解析"易语言申请进程堆栈...

    内存、堆栈详解

    ### 内存、堆栈详解 #### 一、内存分配器(Memory Allocator) 内存分配器是计算机程序中负责管理内存分配的重要组件。在本节中,我们将深入探讨内存分配器的工作原理及其在Go语言中的具体实现。 ##### 1.1 基于...

    Java堆栈内存分析笔记

    Java堆栈内存分析是Java编程中的重要概念,它关乎程序的性能优化和内存泄漏的预防。堆和栈是Java内存管理的两个主要区域,它们各自承担着不同的职责。本笔记将深入探讨这两个区域的工作原理以及如何进行有效的分析。...

    STM32堆栈内存分析

    ### STM32堆栈内存分析 #### 一、引言 在嵌入式开发中,了解内存管理机制对于编写高效且可靠的程序至关重要。本篇文章主要针对STM32微控制器的堆栈内存进行深入分析,旨在帮助开发者理解STM32如何管理和使用内存...

    gflags x86 x64 堆栈溢出内存检测工具

    《gflags:x86与x64架构下的堆栈溢出内存检测工具详解》 在软件开发过程中,确保程序的稳定性和安全性至关重要。而堆栈溢出是导致程序崩溃和安全漏洞的常见原因之一。为了有效地检测和预防这类问题,开发者可以使用...

    Delphi枚举内存堆.rar

    本文将深入探讨如何使用Delphi来枚举指定进程的内存堆,了解内存堆栈ID、内存基地址、占用内存大小以及相关的内存标志。我们将基于提供的资源——"Delphi枚举内存堆.rar"中的代码来展开讨论。 首先,我们需要明白...

    堆栈内存区别

    ### 堆栈内存区别详解 在计算机编程与运行环境中,堆栈(Heap vs Stack)是两种关键的数据存储区域,它们各自拥有独特的属性与管理方式,对于程序的性能与资源管理有着深远的影响。本文将深入解析堆栈的区别,探讨...

    堆栈与内存空间

    对堆栈和内存空间的详细描述,哪些是变量是在静态数据区分配的,动态分配是在堆上分配的

    国外部分手机参数

    【标题】:“国外部分手机参数”讨论了手机的性能参数,特别是与JAVA应用程序相关的内存堆栈大小和单个JAR文件的最大支持。 【描述】:本文档提供了关于JAVA堆栈和内存管理的深入理解,强调了JAVA内存堆栈对手机上...

    易语言申请进程堆栈内存源码

    本话题主要围绕“易语言申请进程堆栈内存源码”这一主题展开,我们将深入探讨易语言如何处理进程、堆栈以及内存申请等核心概念。 首先,我们需要理解什么是进程。在操作系统中,进程是程序的一次执行实例,包含了...

    Java中内存泄露及垃圾回收机制参照.pdf

    在Java中,对象的建立和放置都是在内存堆栈上面进行的。程序或者其他的对象可以锁定一块堆栈地址来进行其他对象的引用。当一个对象没有任何引用的时候,Java的自动垃圾回收机制就发挥作用,自动删除这个对象所占用的...

    DrMemory-Windows-1.11.0-2_c++应用程序内存检测工具_

    DrMemory是由动态分析工具套件Peach Pit和惠普实验室共同开发的开源工具,它的主要功能是检查内存堆栈信息,发现并报告内存越界、空指针和野指针等常见的内存错误。通过深入分析程序运行时的内存行为,DrMemory可以...

    华中科技大学计算机学院计算机组成原理实验

    存储器分为内存(RAM)和外存(ROM、硬盘等)。学生可能需要模拟随机存取存储器的工作原理,理解读写操作,以及了解不同类型的存储器(如SRAM、DRAM)的特性。 6. **CPU实验(定长指令,现代时序,中断)**: CPU...

    JCL_Debug_StackTrace_Demo

    JCL通过分析内存堆栈,收集函数调用的历史,然后将这些信息转换为易于理解的字符串格式,这使得调试工作变得直观而高效。 在实际操作中,使用JCL进行堆栈跟踪通常涉及到以下步骤: 1. 引入JCL库:将JCL的单元文件...

    CDMA软件介绍 程序内存和堆栈

    在CDMA终端开发中,程序内存和堆栈的管理至关重要,因为这些资源通常是有限的,尤其是在内存容量仅为8M或16M的设备上。针对这种情况,开发者需要谨慎对待内存的分配与使用,以防止系统崩溃。 首先,程序内存的需求...

    头歌实践教学平台 MIPS流水CPU设计---HUST

    本实验从 MIPS 单周期 CPU 开始逐步构建无冲突冒险的理想指令流水线,能处理分支相关的指令流水线,采用气泡处理数据相关的气泡式流水线...第9关:多级嵌套中断(EPC内存堆栈保存).txt (其余关卡还在持续更新当中……)

    关于堆栈、静态、动态内存的理解

    一个正常的程序在内存中通常分为程序段、数据端、堆栈三部分。程序段里放着程序的机器码、只读数据,这个段通常是只读,对它的写操作是非法的。数据段放的是程序中的静态数据。动态数据则通过堆栈来存放。 在内存中...

    头歌实践教学平台 MIPS CPU设计(HUST)

    本实训项目将帮助学生掌握 CPU 控制器设计的基本原理,能利用硬布线控制器的设计原理在 Logisim 平台中设计实现支持五条指令的 MIPS 单周期 CPU,该处理器能...第8关:单周期CPU多级中断机制设计---内存堆栈(构建中)

Global site tag (gtag.js) - Google Analytics