转自天极网,URL=http://dev.yesky.com/346/8269346.shtmlhtml
不少实时嵌入式设备是长时间不间断运行的,即便是少量的内存泄漏,也会聚沙成塔,对嵌入式系统带来灾难性的影响。这几天,我在嵌入式软件项目中就饱尝到这个痛苦,让我明白到嵌入式实时系统的应用软件也会有许多内存问题,从而致使嵌入式系统的崩溃。例如非法的内存访问、各类死锁以及诸如堆栈溢出、数组越界和内存泄漏等。程序员
Windows CE做为最流行的一种嵌入式操做系统,现正普遍被应用。我所负责的嵌入式应用程序也是在Windows CE平台上开发的。在进入测试阶段中,我发现有一个程序模块系统内存和CPU资源消耗急剧增长,持续增加到出现OutOfMemoryError为止,而后自动重启。这个问题折腾到我生不如死,痛苦不堪。花了我好几个通宵达旦的加班后,通过分析终于确认Windows CE内存泄漏是形成此次Windows CE系统崩溃的主要缘由。这里与你们分享我在开发过程当中遇到的内存泄漏的检测和处理解决过程。
一.Windows CE如何进行内存分配?
为了判断是否有内存泄露,咱们首先须要了解Windows CE是如何管理内存的。许多嵌入式程序员都有一个共识,就是若是评选在Windows CE 程序中遇到最多的问题,那其中一个问题必定有内存问题。
(1)什么是Windows CE内存管理
通常来讲,运行Windows CE的嵌入式设备出于紧凑型的考虑内存都不大,以致于有时候有些程序员会为了节省内存开支而牺牲程序的某些性能。但尽管WinCE系统的内存很小,用来管理内存的函数却十分完善。Windows CE实现了Windows XP中几乎所有的Win32内存管理API。例如,Windows CE支持虚拟内存分配,本地和分离的堆管理,甚至还有内存映射文件。像Windows XP同样,Windows CE支持带有应用程序间内存保护功能的32位地址空间,这一点对于多程序和多线程运行时是很是重要的功能。可是Windows CE毕竟是被设计来应用于实时场合的,因此它底层的内存结构又不一样于Windows XP。
Windows CE内核能够在Flash上直接运行,也能够加载到内存中运行。Flash的运行方式,是把内核的可执行映像烧写到Flash上,系统启动时从Flash的某个地址开始执行。在这种状况下,Windows CE系统就像直接读硬盘,存储在Flash上的程序可以以现场执行的方式运行。这种能力对小型系统来讲使之在具备巨大的优点,这样这能快速启动一个应用程序,所以这种方法被不少嵌入式系统所采用。另外一种是内核加载方式,是把内核的压缩文件存放在Flash上,系统启动时读取压缩文件在内存里解压,而后开始执行。
(2)虚拟内存和函数应用
和大多数现代操做系统同样,Windows CE实现按需调页的虚拟内存机制。因为Windows CE系统使用了虚拟内存,这就给应用程序形成了一个假象,觉得计算机安装的内存远远超过本身所须要的数量。Windows CE是32位的操做系统,所以支持4GB的虚拟地址空间。Windows把这些地址空间分给进程和系统使用,每一个部分能够得到2GB的虚拟内存。
虚拟内存是内存类型中最基础的。Windows CE 实现了系统的虚拟内存管理,在一个虚拟内存系统中,应用程序主要处理这个虚拟的地址空间,并不涉及到由硬件管理的物理内存。系统调用虚拟内存API来为其它类型内存分配内存,包括堆和栈。Windows CE虚拟内存页能够处在三种状态:自由(free),保留(reserved),或被提交(committed)。
简单说,就是当一个应用程序要查询系统的内存时,可以使用虚拟内存API,包括VirtualAlloc,VirtualFree和VirtualReSize函数,这些函数能够直接操做虚拟内存空间的虚拟内存页面。例如,页面能够保留,提交给物理内存,或使用这些函数释放。Windows CE实现了Win32的GetSystemInfo和GlobalMemoryStatus函数。另外一个检测系统状态的函数是:void GlobalMemoryStatus(LPMEMORYSTATUS lpmst),经过GlobalMemoryStatus返回的信息能够验证Windows CE内存结构。
(3)释放虚拟内存
不一样于Windows XP,Windows CE只支持在堆中分配固定(fixed)的块。这简化了内存块在堆中的处理,可是这使得堆在分配和释放一段时间后会产生碎片。当堆里已经清空的时候,仍然会占用大量的虚拟内存页,由于系统不能在堆中内存页没有彻底释放的时候回收这些页。这时,通常状况下是能够经过调用VirtualFree来取消提交,或释放虚拟内存。从物理RAM页中取消提交或者取消映射,可是保持页被保留的状态,当在区域中的全部的页经过VirtualFree被释放时,也应该处在一样的状况下。更确切地说,区域中的所有页要被释放,那这些页要么都是被提交的页,要么都是被保留的页。若是有些页被提交,有些页被保留,那么VirtualFree函数调用就会失败。
实际上,Windows CE会监视系统自由的内存,并对愈来愈少的内存做出响应。当不多内存可用时,Windows CE首先发送WM_HIBERNATE消息,接下来会限制可能的内存分配。当应用程序被发送了一个WM_HIBERNATE消息后,系统将检测内存级别,确认是否可用内存在限度之上,若是可用内存不足,WM_HIBERNATE消息将被发送给下一个程序,这会持续到全部程序被发送了WM_HIBERNATE消息。
二. 什么是Windows CE内存泄露
虽然Windows CE有许多方法来管理系统内存的运行,但仍是有可能发生内存错误的。Win32编程中常见内存错误:①内存分配错误;②使用未初始化的内存;③内存泄露;④使用已经释放的内存资源。
(1)什么是内存泄漏
内存泄漏是指程序在运行过程当中申请的内存,在程序结束时没有被释放。咱们常说的内存泄漏是指堆内存的泄漏,堆内存是指程序从堆中分配的。通常来讲,应用程序是使用从堆中分配到一块内存,使用完后程序必须负责相应的释放该内存块。不然,这块内存就不能被再次使用,咱们就说这块内存泄漏了。
通常来讲,在全部时刻Windows CE内存管理器都知道进程所拥有的物理内存和虚拟内存。然而,若是进程分配内存时但因为Bug而没法释放内存(内存泄漏),内存管理器就可能没法了解这些已分配的内存,也没法从新访问这些内存,而必须等到进程退出时回收内存。但须要特别注意的是,一样的程序在Window XP平台上可能没有什么问题,但在缺少内存的Windows CE平台,通过长时间运行该程序可能会内存耗尽而致使系统重启,这是我在通过几个生不如死的通宵达旦测试后获得的宝贵经验和教训。
所以,内存泄漏引起的性能失常彻底不一样于程序错误,这些问题很难经过调试器对代码进行单步调试加以解决。对于将会在某时刻退出的桌面应用程序,较小的内存泄漏是能够承受的,由于退出进程将把占用的全部内存返还给操做系统。但对于长时间运行的嵌入式系统,则一般须要确保绝对没有内存泄漏。
(2)常见的内存泄漏缘由
常见的内存泄漏有这几种缘由:①Windows CE内存碎片。②在局部堆申请的堆只增长不会立刻减小,直到程序退出。③程序运行时分配物理内存,当程序使用完后,这些物理内存仍然被占用,直到系统内存不足时分页内存交换到分页文件中,而后才释放掉其占用的物理内存。④Windows CE内存管理的缺陷。
总而言之,内存泄漏产生的主要缘由是保留了却再也不使用的内存空间。Windows CE虽然有自动管理内存的功能,但内存泄漏也是不容忽视,它每每是破坏嵌入式系统稳定性的重要因素。
三. 如何检测和处理内存泄漏?
如何查找引发内存泄漏的缘由,通常有两个步骤:第一是安排有经验的编程人员对代码进行走查和分析,找出内存泄漏发生的位置。第二是使用专门的内存泄漏测试工具进行测试。
(1)代码走读检测内存泄漏
一般在怀疑发生内存泄漏以后,第一步是要弄清楚泄漏了什么数据和引发了什么泄漏。通常说来,一个正常的系统在其运行稳定后其内存的占用量是基本稳定的,不该该是无限制的增加的。根据这样的基本假设,咱们持续地观察系统运行时使用的内存的大小,若是内存的大小持续地增加,则说明系统存在内存泄漏。
内存泄漏可经过代码走读来发现和定位,也能够用专用的工具来测试和定位。实际上,对于内存泄漏,代码检查有时能比采用任何技术解决方案更快地找到问题所在。预防内存泄漏的惟一方法就是要求程序员在程序结束时,必须将每一个申请的内存都释放。
(2)使用工具检测内存泄漏
一旦知道确实发生了内存泄漏,就须要更专业的工具来查明为何会发生泄漏。在这个时候,咱们一般须要使用一些开销较低的工具来监控和查找内存泄漏。查找内存泄漏的工具不少,最经常使用的释放工具就是dmalloc和mpatrol,这些工具提供了记录并检查全部内存分配的调试版堆栈,从而有利于分析内存泄漏和悬挂指针。
检测内存泄漏的关键是要能截获住对分配内存和释放内存的函数的调用,当截获住这两个函数,咱们就能跟踪每一块内存的生命周期。好比,每当成功的分配一块内存后,就把它的指针加入一个全局的list中;每当释放一块内存,再把它的指针从list中删除。这样,当程序结束的时候,list中剩余的指针就是指向那些没有被释放的内存。哪么,最简单的内存泄漏检测方式就是截获住这些指针。
编程
总的来讲,不管那种方式,咱们都须要认真检查应用程序任何内存分配调用的返回代码,由于在Windows CE中比在桌面版本的Windows中有更多的机会致使内存分配失败,从而会致使内存泄漏。数组
PS:我本身的vb.net写的WINCE程序运行一个月左右就会瘫痪掉,如今正在找具体缘由,找到缘由解决了问题,会在博客中具体贴出。多线程