内存对很多人来说感觉是个很熟悉的东西,因为我们在用VC调试程序时,很多时候都会察看内存中变量的值。但是,很多时候我们的思维也就因此局限在有源码的模块,当遇到一些跨模块或是没有源代码的Bug,我们还是无从下手。因此,很有必要我们要对整个程序内存有个比较全局性的认识,这样遇到任何问题,你都能从容面对。
我这里以32位的程序为例,我们知道32位程序总共有4G内存空间,其中低2G是用户地址空间,高2G是内核地址空间,下面我们借助WinDbg工具来分析低2G用户地址空间的内存分布。
因为所有程序的内存分布都大同小异,我这里用WinDbg分析任务管理器进程的内存分布。打开任务管理器,然后用WinDbg Attach到taskmgr.exe进程。
输入!address -summary 察看内存的使用情况, 结果如下:
从上图可以看到,程序内存根据使用情况大致分为:
Free - 没有被使用的
Image - 加载到内存的模块(dll,exe等)
MappedFile - 内存映射文件
unclassified - 实际上应该是堆(heap)
Stack - 堆栈
TEB - 线程环境块(thread environment block)
PEB - 进程环境块(process environment block)
内存根据使用类型又可以分为:
MEM_IMAGE - 加载到内存的模块(dll, exe等)
MEM_MAPPED - 内存映射
MEM_PRIVATE - 私有(stack, heap, teb, peb等)
内存根据使用状态又可分为:
MEM_FREE - 空闲
MEM_COMMIT - 已经提交
MEM_RESERVE - 保留
根据页面属性又可分为只读,可读写,可执行,写时拷贝等。
实际上我们可以通过!address命令来查看更详细的内存使用情况:
可以看到上面列出了所有2G用户空间的页面使用情况(截图只是开始的一部分),我们可以根据某个地址来分析该地址属于那块内存区域。当然也可以通过命令来分析某个地址所属的内存区域, 比如输入!address 7c554来分析地址7c554的情况,会显示:
上面告诉我们7c554是某个堆栈(Stack)空间的地址.
对我们程序来说最常接触的内存应该是: Module, Heap, Stack,接下来依次分析.
(1)Module
Module在上面被叫住Image,实际上就是被加载到内存的Exe和DLL文件, 我们可以通过lm命令来查看所有的模块分布情况:
上面可以看到每个模块的内存起始地址,那么各个模块具体内部又是如何分布,它和磁盘上的DLL(exe)文件又是什么关系呢?
实际上内存的中DLL和磁盘上的DLL文件非常相似,系统在加载时只是根据页面大小(一般4K)作了一些对齐,另外有些数据节如果运行时用不到(比如dll的重定位节)就不会被加载.
我们在!address查看内存空间时,可以看到taskmgr.exe模块的内存分布如下:
上面可以看到taskmgr.exe模块在内存中分为4块,第一块是只读的, 实际上是PE文件头;第二块是可执行的,实际上就是代码节(.text);第三块是可读写的,实际上数据节(.Data); 最后一块也是只读的,实际上资源节(.rsrc)。
要详细的了解taskmgr.exe模块的文件头属性,可以通过!dh [module address]来查看, 输入!dh1000000,查看结果:
上面的运行结果可以验证我们关于taskmgr.exe模块内部分布的猜想.
(2)Heap
Heap实际上就是堆,我们所有new(malloc)出来的内存就是分布在堆里,每个程序会有若干个堆,有些是系统创建的,也有的是C/C++运行库创建的,当然我们自己也可以创建私有堆.我们可以通过!heap命令来查看堆的使用情况.
可以看到taskmgr.exe一共有9个堆。
!heap命令非常强大,通过开启页堆功能,可以很方便的让我们跟踪所有堆内存的分配和使用情况,以后有机会再细说heap相关的.
(3)Stack
Stack即我们通常所说的栈,我们的局部变量就是分配在栈上面。说到栈就要说到线程,我们的代码都是通过线程跑起来的,每个线程包含2块东西,一块是线程内核对象,还有一块就是堆栈,线程运行过程也是堆栈不断压栈和出栈的过程。
我们可以通!address -f:stack 来查看堆栈的分布情况:
从上图我们可以看到taskmgr.exe一共有4个线程, 对应着4个堆栈, 同时也可以看到每个堆栈内存的起始地址。
如果有兴趣,我们也可以看下每个线程的堆栈情况, 输入~* kp
可以看到相应的4个线程堆栈,最后一个线程(debugBreakPoint)看起来有些奇怪,实际上它是调试器为调试而插入的,不是真正的属于taskmgr.exe, 所以任务管理器实际上一共应该有3个线程.
通过上面的介绍,相信大家对程序内存有了比较全局的理解,以后大家分析问题,遇到一个地址,首先要判断这个地址分布在哪里:
如果是Image上,那么是在哪个模块中,这个地址是属于该模块的代码段(.text)还是数据段(.data),如果是代码段,又是属于哪个函数?
如果是Heap上,那么究竟是在哪个堆里面,是我们new出来的吗,是在什么时候new的(new时堆栈状况)?
如果是在Stack上,那么究竟是属于哪个线程的堆栈,当时线程的堆栈是怎么样?
总之,程序在内存中运行,只有你真正理解了内存,你才能真正懂计算机。
相关推荐
2. **内存映射视图**:提供一个清晰的内存布局,显示进程的各个内存段,包括代码区、数据区、堆区等,帮助用户理解程序内存占用情况。 3. **数据搜索与分析**:可以快速搜索内存中的特定数据,如字符串、数值等,...
深入理解Java内存模型对于编写高效的并发程序至关重要。本文将详细介绍JMM的核心概念、工作原理以及相关的编程实践。 1. **核心概念** - **线程私有区域**: 包括程序计数器、虚拟机栈、本地方法栈,这些区域中的...
通过这款软件,用户可以深入理解程序内存管理的细节,辅助进行调试和优化工作。下面我们将详细探讨该内存查看器涉及的核心知识点。 1. **易语言**:易语言是一种中国本土开发的、面向对象的、中文编程语言,旨在...
总结起来,"程序内存分析工具"主要关注的是利用工具如Vmmap来理解和优化程序的内存使用。通过对进程内存的深入分析,开发者能够识别内存泄漏、提高资源利用率、减少性能消耗,并确保程序的稳定性和效率。提供的文件...
2. **内存布局**:理解程序内存的堆、栈和静态区的布局对于定位目标数据至关重要。堆用于动态分配内存,栈用于存储函数调用时的局部变量,而静态区则存放全局变量和常量。 3. **进程与线程**:内存修改器需要找到...
深入理解程序设计.使用Linux汇编语言.高清.完整版 是否真正理解汇编语言,常常是普通程序员和优秀程序员的分水岭。《深入理解程序设计:使用Linux汇编语言》介绍了Linux平台下的汇编语言编程,教你从计算机的角度看...
在计算机系统中,内存是执行程序的关键资源。当应用程序运行时,它们会占用一定的内存空间,用于存储数据、代码和运行时的状态。然而,随着时间的推移或在多任务...因此,理解并掌握内存管理对于提升用户体验至关重要。
"勇芳内存数据分析编辑器"是一款专...总之,"勇芳内存数据分析编辑器"是一款强大且多功能的工具,适合那些需要深入理解程序内存行为的技术人员。通过熟练掌握它的使用,可以极大地提升软件开发、调试和安全审计的效率。
深入学习 JMM 不仅能帮助开发者理解 Java 并发的基础原理,还能提高程序的并发性能,避免因内存可见性问题导致的程序异常。《深入理解 Java 内存模型》这本书详细探讨了这些主题,是 Java 开发者不可或缺的参考资料...
Java运行时环境(JVM)为开发者提供了自动内存管理的便利,但理解其工作原理可以帮助我们编写出更高效的应用程序。本文将分为几个部分来详细探讨这一主题: 1. **Java进程的内存结构** - **操作系统视角**:Java...
本主题聚焦于易语言在处理64位程序内存读写操作方面的应用,这对于理解底层数据处理、游戏修改、软件调试等领域非常重要。 在64位操作系统环境下,程序的内存管理与32位系统有很大不同。64位程序可以访问的内存地址...
Linux虚拟内存管理是操作系统设计中的核心部分,它允许程序访问比实际物理内存更大的地址空间,同时优化了内存的使用效率。这一系统通过映射技术,将进程的虚拟地址转换为实际物理地址,使得多个进程可以共享同一...
以下是关于如何解除32位应用程序内存限制的一些详细步骤: 1. **开启“大型地址空间感知”**:这需要在应用程序的可执行文件属性中进行设置。使用"4gb_patch.exe"这样的工具可能就是为了自动完成这个过程,如果没有...
内存读取工具是一种用于分析和调试计算机程序的重要软件,它允许用户查看并操作正在运行的程序在内存中的状态。在编程、逆向工程、安全分析和故障排查等场景下,这样的工具是不可或缺的。本文将深入探讨内存读取工具...
首先,理解内存驻留程序的工作原理是关键。当一个程序被设置为驻留内存时,它的部分或全部代码会被加载到RAM(随机访问存储器)中,这样即使用户没有直接与该程序交互,它也能随时执行任务。这种设计提高了响应速度...
- **对象引用**:理解程序中的对象如何相互引用是垃圾回收的基础。这可能涉及到跟踪指针、引用计数、甚至是更复杂的图遍历算法。 - **停止世界(Stop-the-world)事件**:在进行垃圾回收时,程序的执行通常会被暂停...
`使用说明.txt`则应该是包含如何使用这个程序和理解虚拟内存相关概念的指南。 在设置虚拟内存时,有几点需要注意: 1. **大小设定**:虚拟内存的大小一般建议设置为物理内存的1.5倍到3倍之间,但不要超过磁盘可用...
首先,我们需要理解程序集(Assembly)的概念。在.NET中,程序集是代码的物理组织单元,包含了元数据和IL(中间语言)代码,可以是DLL或EXE文件。要将程序集加载到内存中,我们可以使用`System.Reflection.Assembly`...
在Windows操作系统中,通常我们通过双击或者...通过学习和理解这部分代码,可以深入理解Windows操作系统的工作原理,以及如何与系统API交互来实现更底层的程序控制。这对于提升系统编程和逆向工程技能非常有帮助。