当前位置:文档之家› Windows内存管理机制

Windows内存管理机制

Windows内存管理机制
Windows内存管理机制

Windows内存管理机制

在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用;根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制。

本文目的:

对Windows内存管理机制了解清楚,有效的利用C++内存函数管理和使用内存。

本文内容:

本文一共有六节,由于篇幅较多,故按节发表。其他章节请看本人博客的Windows内存管理及C++内存分配实例(一)(二)(三)(四)和(五)。

1. 进程地址空间

2.内存状态查询函数

3.内存管理机制--虚拟内存 (VM)

4.内存管理机制--内存映射文件 (Map)

5.内存管理机制--堆 (Heap)

使用场合

操作系统为每个线程都建立一个默认堆栈,大小为1M。这个堆栈是供函数调用时使用,线程内函数里的各种静态变量都是从这个默认堆栈里分配的。

堆栈结构

默认1M的线程堆栈空间的结构举例如下,其中,基地址为0x0004 0000,刚开始时,CPU的堆栈指针寄存器保存的是栈顶的第一个页面地址

0x0013 F000。第二页面为保护页面。这两页是已经分配物理存储器的可用页面。

随着函数的调用,系统将需要更多的页面,假设需要另外5页,则给这5页提交内存,删除原来页面的保护页面属性,最后一页赋予保护页面属性。

当分配倒数第二页0x0004 1000时,系统不再将保护属性赋予它,相反,它会产生堆栈溢出异常STATUS_STACK_OVERFLOW,如果程序没有处理它,则线程将退出。最后一页始终处于保留状态,也就是说可用堆栈数是没有1M的,之所以不用,是防止线程破坏栈底下面的内存(通过违规访问异常达到目的)。

当程序的函数里分配了临时变量时,编译器把堆栈指针递减相应的页数目,堆栈指针始终都是一个页面的整数倍。所以,当编译器发现堆栈指针位于保护页面之下时,会插入堆栈检查函数,改变堆栈指针及保护页面。

这样,当程序运行时,就会分配物理内存,而不会出现访问违规。

使用例子

改变堆栈默认大小:

有两个方法,一是在CreateThread()时传一个参数进去改变;

二是通过链接命令:

#pragma comment(linker,"/STACK:102400000,1024000")

第一个值是堆栈的保留空间,第二个值是堆栈开始时提交的物理内存大小。本文将堆栈改变为100M。

堆栈溢出处理:

如果出现堆栈异常不处理,则导致线程终止;如果你只做了一般处理,内存

结构已经处于破坏状态,因为已经没有保护页面,系统没有办法再抛出堆栈溢

出异常,这样的话,当再次出现溢出时,会出现访问违规操作

STATUS_ACCESS_VIOLATION,这是线程将被系统终止。解决办法是,恢复

堆栈的保护页面。请看以下例子:

C++程序如下:

bool handle=true;

static MEMORY_BASIC_INFORMATION mi; LPBYTE lpPage;

//得到堆栈指针寄存器里的值

_asm mov lpPage, esp;

// 得到当前堆栈的一些信息

VirtualQuery(lpPage, &mi, sizeof(mi));

//输出堆栈指针

printf("堆栈指针=%x\n",lpPage);

// 这里是堆栈的提交大小

printf("已用堆栈大小=%d\n",mi.RegionSize); printf("堆栈基址=%x\n",mi.AllocationBase);

for(int i=0;i<2;i++)

{

__try

{

__try

{

__try

{

cout<<"**************************"<

//如果是这样静态分配导致的堆栈异常,系统默认不

抛出异常,捕获不到

//char a[1024*1024];

//动态分配栈空间,有系统调用Alloca实现,自动

释放

Add(1000);

//系统可以捕获违规访问

int * p=(int*)0xC00000000;

*p=3;

cout<<"执行结束"<

}

__except(GetExceptionCode()==STATUS_ACCESS_ VIOLATION ? EXCEPTION_EXECUTE_HANDLER :

EXCEPTION_CONTINUE_SEARCH)

{

cout<<"Excpetion 1"<

}

}

__except(GetExceptionCode()==STATUS_STACK_OVERFL OW ? EXCEPTION_EXECUTE_HANDLER :

EXCEPTION_CONTINUE_SEARCH)

{

cout<<"Exception 2"<

if(handle)

{

//做堆栈破坏状态恢复

LPBYTE lpPage;

static SYSTEM_INFO si;

static MEMORY_BASIC_INFORMATION mi;

static DWORD dwOldProtect;

// 得到内存属性

GetSystemInfo(&si);

// 得到堆栈指针

_asm mov lpPage, esp;

// 查询堆栈信息

VirtualQuery(lpPage, &mi, sizeof(mi));

printf("坏堆栈指针=%x\n",lpPage);

// 得到堆栈指针对应的下一页基址

lpPage = (LPBYTE)(mi.BaseAddress)-

si.dwPageSize;

printf("已用堆栈大小=%d\n",mi.RegionSize);

printf("坏堆栈基址=%x\n",mi.AllocationBase); //释放准保护页面的下面所有内存

if (!VirtualFree(mi.AllocationBase,

(LPBYTE)lpPage -

(LPBYTE)mi.AllocationBase,

MEM_DECOMMIT))

{

exit(1);

}

// 改页面为保护页面

if (!VirtualProtect(lpPage, si.dwPageSize,

PAGE_GUARD | PAGE_READWRITE, &dwOldProtect))

{

exit(1);

}

}

printf("Exception handler %lX\n", _exception_code()); }

}

__except(EXCEPTION_EXECUTE_HANDLER)

{

cout<<"Default handler"<

}

}

cout<<"正常执行"<

//分配空间,耗用堆栈

char c[1024*800];

printf("c[0]=%x\n",c);

printf("c[1024*800]=%x\n",&c[1024*800-1]);

}

void ThreadStack::Add(unsigned long a)

{

//深递归,耗堆栈

char b[1000];

if(a==0)

return;

Add(a-1);

}

程序运行结果如下:

可以看见,在执行递归前,堆栈已被用了800多K,这些是在编译时就静态决定了。它们不再占用进程空间,因为堆栈占用了默认的1M进程空间。分配是从栈顶到栈底的顺序。

当第一次递归调用后,系统捕获到了它的溢出异常,然后堆栈指针自动恢复到原来的指针值,并且在异常处理里,更改了保护页面,确保第二次递归调用时不会出现访问违规而退出线程,但是,它仍然会导致堆栈溢出,需要动态的增加堆栈大小,本文没有对这个进行研究,但是试图通过分配另外内存区,改变堆栈指针,但是没有奏效。

注意:在一个线程里,全局变量加上任何一个函数里的临时变量,如果超过堆栈大小,当调用这个函数时,都会出现堆栈溢出,这种溢出系统不会抛出堆栈溢出异常,而直接导致线程退出。

对于函数1调用函数2,而函数n-1又调用函数n的嵌套调用,每层调用不算临时变量将损失240字节,所以默认线程最多有1024*(1024-2)/240=4360次调用。加上函数本身有变量,这个数目会大大减少。

操作系统内存管理复习过程

操作系统内存管理

操作系统内存管理 1. 内存管理方法 内存管理主要包括虚地址、地址变换、内存分配和回收、内存扩充、内存共享和保护等功能。 2. 连续分配存储管理方式 连续分配是指为一个用户程序分配连续的内存空间。连续分配有单一连续存储管理和分区式储管理两种方式。 2.1 单一连续存储管理 在这种管理方式中,内存被分为两个区域:系统区和用户区。应用程序装入到用户区,可使用用户区全部空间。其特点是,最简单,适用于单用户、单任务的操作系统。CP/M和 DOS 2.0以下就是采用此种方式。这种方式的最大优点就是易于管理。但也存在着一些问题和不足之处,例如对要求内

存空间少的程序,造成内存浪费;程序全部装入,使得很少使用的程序部分也占用—定数量的内存。 2.2 分区式存储管理 为了支持多道程序系统和分时系统,支持多个程序并发执行,引入了分区式存储管理。分区式存储管理是把内存分为一些大小相等或不等的分区,操作系统占用其中一个分区,其余的分区由应用程序使用,每个应用程序占用一个或几个分区。分区式存储管理虽然可以支持并发,但难以进行内存分区的共享。 分区式存储管理引人了两个新的问题:内碎片和外碎片。 内碎片是占用分区内未被利用的空间,外碎片是占用分区之间难以利用的空闲分区(通常是小空闲分区)。 为实现分区式存储管理,操作系统应维护的数据结构为分区表或分区链表。表中各表项一般包括每个分区的起始地址、大小及状态(是否已分配)。

分区式存储管理常采用的一项技术就是内存紧缩(compaction)。 2.2.1 固定分区(nxedpartitioning)。 固定式分区的特点是把内存划分为若干个固定大小的连续分区。分区大小可以相等:这种作法只适合于多个相同程序的并发执行(处理多个类型相同的对象)。分区大小也可以不等:有多个小分区、适量的中等分区以及少量的大分区。根据程序的大小,分配当前空闲的、适当大小的分区。 优点:易于实现,开销小。 缺点主要有两个:内碎片造成浪费;分区总数固定,限制了并发执行的程序数目。 2.2.2动态分区(dynamic partitioning)。 动态分区的特点是动态创建分区:在装入程序时按其初始要求分配,或在其执行过程中通过系统调用进行分配或改变分区大小。与固定分区相比较其优点是:没有内碎

linux内存管理子系统 笔记

4-4 linux内存管理子系统 4-4-1 linux内存管理(参考课件) 物理地址:cpu地址总线上寻址物理内存的地址信号,是地址变换的最终结果 逻辑地址:程序代码经过编译后,出现在汇编程序中的地址(程序设计时使用的地址) 线性地址:又名虚拟地址,32位cpu架构下4G地址空间 CPU要将一个逻辑地址转换为物理地址,需要两步: 1、首先CPU利用段式内存管理单元,将逻辑地址转换成线性地址; 2、再利用页式内存管理单元,把线性地址最终转换为物理地址 相关公式: 逻辑地址=段基地址+段内偏移量(段基地址寄存器+段偏移寄存器)(通用的) 16位CPU:逻辑地址=段基地址+段内偏移量(段基地址寄存器+段偏移寄存器) 线性地址=段寄存器的值×16+逻辑地址的偏移部分 物理地址=线性地址(没有页式管理) 32位CPU:逻辑地址=段基地址+段内偏移量(段基地址寄存器+段偏移寄存器) 线性地址=段寄存器的值+逻辑地址的偏移部分 物理地址<——>线性地址(mapping转换) ARM32位:逻辑地址=段基地址+段内偏移量(段基地址寄存器+段偏移寄存器) 逻辑地址=段内偏移量(段基地址为0) 线性地址=逻辑地址=段内偏移量(32位不用乘以32) 物理地址<——>线性地址(mapping转换) ************************!!以下都是x86模式下!!********************************* 一、段式管理 1.1、16位CPU:(没有页式管理) 1.1.1、段式管理的由来: 16位CPU内部有20位地址总线,可寻址2的20次方即1M的内存空间,但16位CPU 只有16位的寄存器,因此只能访问2的16次方即64K。因此就采用了内存分段的管理模式,在CPU内部加入了段寄存器,这样1M被分成若干个逻辑段,每个逻辑段的要求如下: 1、逻辑段的起始地址(段地址)必须是16的整数倍,即最后4个二进制位须全是0 (因此不必保存)。 2、逻辑段的最大容量为64K。 1.1.2、物理地址的形成方式: 段地址:将段寄存器中的数值左移4位补4个0(乘以16),得到实际的段地址。 段偏移:在段偏移寄存器中。 1)逻辑地址=段基地址+段内偏移量(段基地址寄存器+段偏移寄存器) 2)由逻辑地址得到物理地址的公式为:(因为没有页式管理,所以这一步就得到了物理地址)物理地址PA=段寄存器的值×16+逻辑地址的偏移部分(注意!!)(段与段可能会重叠)

动态内存管理知识总结

1.标准链接库提供四个函数实现动态内存管理: (1)分配新的内存区域: void * malloc(size_t size); void *calloc(size_t count , size_t size); (2)调整以前分配的内存区域: void *realloc(void *ptr , size_t size); (3)释放以前分配的内存区域: void free(void *ptr); 2.void * malloc(size_t size); 该函数分配连续的内存空间,空间大小不小于size 个字节。但分配的空间中的内容是未知的。该函数空间分配失败则返回NULL。 3.void *calloc(size_t count , size_t size); 该函数也可以分配连续的内存空间,分配不少于count*size个字节的内存空间。即可以为一个数组分配空间,该数组有count个元素,每个元素占size个字节。而且该函数会将分配来的内存空间中的内容全部初始化为0 。该函数空间分配失败则返回NULL。 4. 以上两个分配内存空间的函数都返回void * (空类型指针或无类型指针)返回的指针值是“分配的内存区域中”第一个字节的地址。当存取分配的内存位置时,你所使用的指针类型决定如何翻译该位置的数据。以上两种分配内存空间的方法相比较,calloc()函数的效果更好。原因是它将分配得来的内存空间按位全部置0 。 5. 若使用上述两种分配内存的函数分配一个空间大小为0 的内存,函数会返回一个空指针或返回一个没有定义的不寻常指针。因此绝不可以使用“指向0 字节区域”的指针。 6. void *realloc(void *ptr , size_t size); 该函数释放ptr所指向的内存区域,并分配一个大小为size字节的内存区域,并返回该区域的地址。新的内存区域可以和旧的内存区域一样,开始于相同的地址。且此函数也会保留原始内存内容。如果新的内存区域没有从原始区域的地址开始,那么此函数会将原始的内容复制到新的内存区域。如果新的内存区域比较大,那么多出来部分的值是没有意义的。 7. 可以把空指针传给realloc()函数,这样的话此函数类似于malloc()函数,并得到一块内存空间。如果内存空间不足以满足内存区域分配的请求,那么realloc()函数返回一个空指针,这种情况下,不会释放原始的内存区域,也不会改变它的内容。 8. void free(void *ptr); 该函数释放动态分配的内存区域,开始地址是ptr,ptr的值可以是空指针。若在调用此函数时传入空指针,则此函数不起任何作用。 9. 传入free() 和realloc()函数的指针(若不为空指针时)必须是“尚未被释放的动态分配内存区域的起始地址”。否则函数的行为未定义。Realloc()函数也可以释放内存空间,例如:Char *Ptr = (char *)malloc(20); 如只需要10个字节的内存空间,且保留前十个字节的内容,则可以使用realloc()函数。 Ptr = Realloc(ptr,10); // 后十个字节的内存空间便被释放

全面介绍Windows内存管理机制

全面介绍Windows内存管理机制及C++内存分配实例 文章整理: https://www.doczj.com/doc/2316025667.html, 文章来源: 网络- - 本文背景: 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用;根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制。 本文目的: 对Windows内存管理机制了解清楚,有效的利用C++内存函数管理和使用内存。本文内容: 本文一共有六节,由于篇幅较多,故按节发表。 1.进程地址空间 1.1地址空间 ?32|64位的系统|CPU 操作系统运行在硬件CPU上,32位操作系统运行于32位CPU 上,64位操作系统运行于64位CPU上;目前没有真正的64位CPU。 32位CPU一次只能操作32位二进制数;位数多CPU设计越复杂,软件设计越简单。 软件的进程运行于32位系统上,其寻址位也是32位,能表示的空间是232=4G,范围从0x0000 0000~0xFFFF FFFF。 ?NULL指针分区 范围:0x0000 0000~0x0000 FFFF 作用:保护内存非法访问 例子:分配内存时,如果由于某种原因分配不成功,则返回空指针0x0000 0000;当用户继续使用比如改写数据时,系统将因为发生访问违规而退出。 那么,为什么需要那么大的区域呢,一个地址值不就行了吗?我在想,是不是因为不让8或16位的程序运行于32位的系统上呢?!因为NULL分区刚好范围是16的进程空间。 ?独享用户分区 范围:0x0001 0000~0x7FFE FFFF 作用:进程只能读取或访问这个范围的虚拟地址;超越这个范围的行为都 会产生违规退出。 例子: 程序的二进制代码中所用的地址大部分将在这个范围,所有exe 和dll文件都加载到这个。每个进程将近2G的空间是独享的。 注意:如果在boot.ini上设置了/3G,这个区域的范围从2G扩大为3G: 0x0001 0000~0xBFFE FFFF。 ?共享内核分区 范围:0x8000 0000~0xFFFF FFFF 作用:这个空间是供操作系统内核代码、设备驱动程序、设备I/O高速缓存、非页面内存池的分配、进程目表和页表等。 例子: 这段地址各进程是可以共享的。

浅谈Android(安卓)

浅谈Android--嵌入式操作系统 Android(读音:[??ndr?id],中文俗称安卓)是一个以Linux为基础的半开源操作系统,主要用于移动设备,由Google成立的Open Handset Alliance (OHA,开放手持设备联盟)持续领导与开发中。 --题记.维基百科说起嵌入式系统,曾经在保罗大叔的著作《黑客与画家》里看到多次,然后不明所以,就去查了嵌入式系统。如果说嵌入式系统给我的第一印象是硬件,那么是我还不知道嵌入式在我生活里已经出现了很多年了。 大到冰箱,自动存款机(ATM),小到电子手表,遥控器。在维基百科解答后,我对嵌入式直观的理解,是一种特定的植入硬件并极具针对性的计算机系统。 后来慢慢了解嵌入式的软件方面,就知道了嵌入式操作系统,而其中的佼佼者,就是如今已经超越ios,占据半壁江山的Android。 之所以会说Android,原因有二:一,因为Android如今炙手可热,在新一季度的日本手机软件营销额上,以Java等语言为Android系统开发的Apps,疯狂揽金,李开复断言在两年内,中国内地手机游戏软件市场,将会百花齐放;二,我虽并非研究Java也非致力于Android系统,但是Android系统的内核,却是我所熟悉的Linux内核。而我将自己的开发平台转移到Linux系统,并以Python,Perl以及Lisp语言作为未来的生存工具,所以,就让我们谈一谈Android。 题记中套用维基百科对于Android的介绍,主要的目的,就是为了澄清一件事实“认知”——Android并没有真正的中文名。 Google并没有为Android命名,只有为其版本取名,且翻译成中文:4.2.x Jelly Bean 果冻豆,4.0.x Ice Cream Sandwich 冰激凌三明治,3.x.x Honey

ANDROID BITMAP内存限制OOM,OUT OF MEMORY

ANDROID BITMAP内存限制OOM OUT OF MEMORY 在编写Android程序的时候,我们总是难免会碰到OOM的错误,那么这个错误究竟是怎么来的呢?我们先来看一下这段异常信息: 08-14 05:15:04.764: ERROR/dalvikvm-heap(264): 3528000-byte external allocation too large for this process. 08-14 05:15:04.764: ERROR/(264): VM won't let us allocate 3528000 bytes 08-14 05:15:04.764: DEBUG/skia(264): --- decoder->decode returned false 08-14 05:15:04.774: DEBUG/AndroidRuntime(264): Shutting down VM 08-14 05:15:04.774: WARN/dalvikvm(264): threadid=3: thread exiting with uncaught exception (group=0x4001b188) 08-14 05:15:04.774: ERROR/AndroidRuntime(264): Uncaught handler: thread main exiting due to uncaught exception 08-14 05:15:04.794: ERROR/AndroidRuntime(264): https://www.doczj.com/doc/2316025667.html,ng.OutOfMemoryError: bitmap size exceeds VM budget 08-14 05:15:04.794: ERROR/AndroidRuntime(264): at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method) 08-14 05:15:04.794: ERROR/AndroidRuntime(264): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:447) 08-14 05:15:04.794: ERROR/AndroidRuntime(264): at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:323) 08-14 05:15:04.794: ERROR/AndroidRuntime(264): at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:346) 08-14 05:15:04.794: ERROR/AndroidRuntime(264): at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:372) 08-14 05:15:04.794: ERROR/AndroidRuntime(264): at com.xixun.test.HelloListView.onCreate(HelloListView.java:33) 08-14 05:15:04.794: ERROR/AndroidRuntime(264): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047) 08-14 05:15:04.794: ERROR/AndroidRuntime(264): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2459) 08-14 05:15:04.794: ERROR/AndroidRuntime(264): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2512) 08-14 05:15:04.794: ERROR/AndroidRuntime(264): at android.app.ActivityThread.access$2200(ActivityThread.java:119) 08-14 05:15:04.794: ERROR/AndroidRuntime(264): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1863) 08-14 05:15:04.794: ERROR/AndroidRuntime(264): at android.os.Handler.dispatchMessage(Handler.java:99) 08-14 05:15:04.794: ERROR/AndroidRuntime(264): at android.os.Looper.loop(Looper.java:123) 08-14 05:15:04.794: ERROR/AndroidRuntime(264): at android.app.ActivityThread.main(ActivityThread.java:4363)

Solaris 8内存管理机制研究

Solaris 8内存管理机制研究 吴海燕 戚丽 冯珂 摘 要:寻找性能瓶颈是性能分析中的一项重要任务,内存瓶颈的表现并不像CPU或磁盘那样直接,本文通过对Solaris 8内存管理机制的研究,给出了寻找Solaris 8系统内存瓶颈的方法。 关键词:Solaris 8,内存管理,性能优化 一、问题的提出 清华大学计算机与信息管理中心数据中心现有服务器近百台,其中包括了SUN Fire 15000、SUN Enterprise 5500、SUN Enterprise 5000等大型SUN服务器,Solaris 8是主流操作系统。为了对服务器的资源(如CPU、内存、磁盘、网络)的使用情况进行长期监控,建立性能优化(performance tuning)的基准值,我们开发了一套脚本程序定时采集系统运行参数。在长期的监控中,我们发现Solaris 8系统的空闲内存(freemem)呈现一个有趣的变化规律,如图1所示: 图1 空闲内存(freemem)变化图 图1是某Solaris 8系统(在下文中我们称之为15k-a)自2003年2月份以来的freemem 变化情况,横坐标是时间,纵坐标是freemem的数量,以8K字节为单位。15k-a配置是10路Super SPARCIII CPU,10GB物理内存。从上图可以看到在正常运行时,freemem应该是比较稳定的,15k-a主要是运行数据库,数据库在运行时会占用2G内存作为SGA区使用,因此在通常的负载下,freemem保持在6~7G之间是比较正常的。稳定一段时间后,

15k-a的freemem会持续走低,直到最低值,约为18893×8KMB,然后系统开始回收内存,我们就会看到freemem数量急剧上升。freemem的陡降都发生在凌晨1:00之后,检查系统作业发现每天1:00都会有一个数据库备份脚本开始运行:首先是用“exp”命令给数据库做逻辑备份,然后用“cp”命令把备份出来的文件拷贝到后备存储上。这两个命令都是正常退出,没有任何报错。开始时我们曾怀疑是有内存泄漏,当某一天freemem大幅攀升时,此怀疑被解除了,因为如果有内存泄漏,系统是无法将内存回收回来的。 对于一个物理内存为10GB的系统来说,如果空闲内存(freemem)真的减少到不到二百兆,那将存在着严重的问题。但奇怪的是系统的CPU使用率一直很低,所有进程的反应也很快,系统没有任何资源匮乏的迹象。如何解释这些问题呢,为此我们对Solaris 2.x 的内存管理机制进行了研究。 二、Solaris的内存管理机制 Solaris 8的内存管理为虚拟内存管理。[1]简单地说,虚拟内存就是进程看到比它实际使用的物理内存多得多的内存空间,对于64位的Solaris 8操作系统,进程可以通过8K 大小的段寻址访问2的64次方字节的内存空间,这种8K的段被称为页(page)。传统的UNIX通过进程(pagedaemon)完成虚拟地址和物理地址间的转换,在Solaris中这些是通过一个硬件-MMU(Memory Management Unit)-来实现的。在多处理器系统中,每个CPU 都有自己的MMU。Solaris 8的虚拟存储体系由系统寄存器、CPU CACHE、主存(RAM,物理内存)、外存(磁盘、磁带等)构成。 有两个基本的虚拟内存系统管理模型[2]:交换(swapping)和按需换页(demand paged)模型。交换模型的内存管理粒度是用户进程,当内存不足时,最不活跃的进程被交换出内存(swapping out)。按需换页模型的内存管理粒度是页(page),当内存匮乏时,只有最不经常使用的页被换出。Solaris 8结合使用了这两种内存管理模型,在通常情况下使用按需换页模型,当内存严重不足时,使用交换模型来进行内存释放。 与传统UNIX系统相比,Solaris虚拟内存系统的功能要丰富得多,它负责管理所有与I/O和内存相关的对象,包括内核、用户应用程序、共享库和文件系统。传统的UNIX系统V(System V)使用一个单独的缓冲区来加速文件系统的I/O, Solaris 8则使用虚拟内存系统来管理文件系统的缓存,系统的所有空闲内存都可以被用来做为文件I/O缓存,因为RAM的访问速度比磁盘快得多,所以这样做带来的性能提高是可观的。这也意味着在存在大量文件系统I/O的系统上,空闲内存的数量几乎是0。 了解系统内存被分配到了什么地方,系统在什么情况下进行内存整理是系统管理的重

内存管理

第八章内存管理 1.地址捆绑 输入队列:在磁盘上等待调入内存以便执行的进程形成了输入队列 捆绑是从一个地址到另一个地址的映射。 编译时:如果在编译时就知道进程将在内存中的驻留地址,那么就可生成绝对代码 加载时:如果在编译时并不知到进程将驻留在何处,那么编译器就必须生成可重定位代码执行时:如果进程在执行时可以从一个内存段转移到另一个内存段,那么捆绑必须延迟到执行时才进行 2.逻辑地址(相对地址,虚拟地址):CPU所生成的地址or 用户程序中使用的地址 物理地址(内存地址,绝对地址):内存单元所看到的地址 编译时和加载时的地址捆绑生成相同的逻辑地址和物理地址运行时不同 重定位寄存器(relocation register)即基址寄存器(base register) 内存管理单元(MMU):运行时实现从虚拟地址到物理地址的映射(map)的硬件设备 3.动态加载 ①所有的子程序只有在调用时才被加载 ②提高内存空间使用率,不用的子程序绝不会被装入内存 ③如果大多数代码需要处理异常情况时是非常有用的。 ④不需要操作系统的特别支持,通过程序设计实现。操作系统可能会为程序员提供实现动态装入的库函数。 4.动态链接库 ①动态链接直到执行时才进行链接。 ②利用动态链接,在映象中为每个库函数引用(library-routine reference)包含一个占位程序(stub)。占位程序是一小段代码,它指明了怎样定位驻留在内存中的库函数或函数不在内存中时怎样装入库。占位程序执行时,它检查所需的函数(routine)是否已经在内存中。如果没有,就把函数装入内存。或者以另外一种方式,占位程序用函数地址取代自身并执行这个函数。这样,下一次到达这段代码时,可以直接执行库函数(library routine)而无需动态

操作系统课程设计内存管理

内存管理模拟 实验目标: 本实验的目的是从不同侧面了解Windows 2000/XP 对用户进程的虚拟内存空间的管理、分配方法。同时需要了解跟踪程序的编写方法(与被跟踪程序保持同步,使用Windows提供的信号量)。对Windows分配虚拟内存、改变内存状态,以及对物理内存(physical memory)和页面文件(pagefile)状态查询的API 函数的功能、参数限制、使用规则要进一步了解。 默认情况下,32 位Windows 2000/XP 上每个用户进程可以占有2GB 的私有地址空间,操作系统占有剩下的2GB。Windows 2000/XP 在X86 体系结构上利用二级页表结构来实现虚拟地址向物理地址的变换。一个32 位虚拟地址被解释为三个独立的分量——页目录索引、页表索引和字节索引——它们用于找出描述页面映射结构的索引。页面大小及页表项的宽度决定了页目录和页表索引的宽度。 实验要求: 使用Windows 2000/XP 的API 函数,编写一个包含两个线程的进程,一个线程用于模拟内存分配活动,一个线程用于跟踪第一个线程的内存行为,而且要求两个线程之间通过信号量实现同步。模拟内存活动的线程可以从一个文件中读出要进行的内存操作,每个内存操作包括如下内容: 时间:操作等待时间。 块数:分配内存的粒度。 操作:包括保留(reserve)一个区域、提交(commit)一个区域、释放(release)一个区域、回收(decommit)一个区域和加锁(lock)与解锁(unlock)一个区域,可以将这些操作编号存放于文件。保留是指保留进程的虚拟地址空间,而不分配物理 存储空间。提交在内存中分配物理存储空间。回收是指释放物理内存空间,但在虚拟地址空间仍然保留,它与提交相对应,即可以回收已经提交的内存块。释放是指将物理存储和虚拟地址空间全部释放,它与保留(reserve)相对应,即可以释放已经保留的内存块。 大小:块的大小。 访问权限:共五种,分别为PAGE_READONLY,PAGE_READWRITE ,PAGE_EXECUTE,PAGE_EXECUTE_READ 和PAGE EXETUTE_READWRITE。可以将这些权限编号存放于文件中跟踪线程将页面大小、已使用的地址范围、物理内存总量,以及虚拟内存总量等信息显示出来。

Android开发内存泄漏及检查工具使用培训资料

Android 开发内存泄漏及检查工具使用培 训资料

目录 1内存泄露 (3) 1.1 内存泄露的概念 (3) 1.2 开发人员注意事项 (4) 1.3 Android(java)中常见的引起内存泄露的代码示例 (4) 1.3.1查询数据库没有关闭游标 (6) 1.3.2 构造Adapter时,没有使用缓存的convertView (6) 1.3.3 Bitmap对象不在使用时调用recycle()释放内存 (7) 1.3.4 释放对象的引用 (8) 1.3.5 其他 (9) 2内存泄露的分析工具 (9) 2.1 内存监测工具DDMS --> Heap (9) 2.2 内存分析工具MAT (Memory Analyzer Tool) (10) 2.2.1 生成.hprof文件 (10) 2.2.2 使用MA T导入.hprof文件 (11) 2.2.3 使用MA T的视图工具分析内存 (12)

1内存泄露 Android 应用程序开发以Java语言为主,而Java编程中一个非常重要但却经常被忽视的问题就是内存使用的问题。Java的垃圾回收机制(Garbage Collection 以下简称GC)使得很多开发者并不关心内存使用的生命周期,只顾着申请内存,却不手动释放废弃的内存,而造成内存泄露,引起很多问题,甚至程序崩溃。Android的虚拟机Dalvik VM和java虚拟机JVM没有什么太大的区别,只是在字节码上稍做优化,所以Android应用开发中同样会出现内存泄露的问题。而且由于Android智能平台主要用于嵌入式产品开发,可用的内存资源更加稀少,所以对于我们Android应用开发人员来说,就更该了解Android程序的内存管理机制,避免内存泄露的发生。 1.1 内存泄露的概念 在计算机科学中,内存泄漏(memory leak)指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费。内存泄漏与许多其他问题有着相似的症状,并且通常情况下只能由那些可以获得程序源代码的程序员才可以分析出来。然而,有不少人习惯于把任何不需要的内存使用的增加描述为内存泄漏,严格意义上来说这是不准确的。 一般我们常说的内存泄漏是指堆内存的泄漏。堆内存是指程序从堆中分配的,大小任意的(内存块的大小可以在程序运行期决定),使用完后必须显式释放的内存。应用程序一般使用malloc,calloc,realloc,new等函数从堆中分配到一块内存,使用完后,程序必须负责相应的调用free或delete释放该内存块,否则,这块内存就不能被再次使用,我们就说这块内存泄漏了。 这里我们只简单的理解,在java程序中,如果已经不再使用一个对象,但是仍然有引用指向它,GC就无法收回它,当然该对象占用的内存就无法再被使用,这就造成内存泄露。可能一个实例对象的内存泄露很小,并不会引起很大的问题。但是如果程序反复做此操作或者长期运行,造成内存不断泄露,终究会使程序无内存可用,只好被系统kill掉。在以下情况,内存泄漏导致较严重的后果: * 程序运行后置之不理,并且随着时间的流失消耗越来越多的内存(比如服务器上的后台任务,尤其是嵌入式系统中的后台任务,这些任务可能被运行后很多年内都置之不理); * 新的内存被频繁地分配,比如当显示电脑游戏或动画视频画面时; * 程序能够请求未被释放的内存(比如共享内存),甚至是在程序终止的时候; * 泄漏在操作系统内部发生; * 泄漏在系统关键驱动中发生; * 内存非常有限,比如在嵌入式系统或便携设备中; * 当运行于一个终止时内存并不自动释放的操作系统(比如AmigaOS)之上,而且一旦丢失只能通过重启来恢复。

Android的原理:关于应用自启动,占内存那些问题

Android的原理-不需要太多的剩余内存 Android用RAM的方式,跟windows、WM、Sybiam是两回事。在Android里,RAM被用满了是件好事。它意味着你可以快速打开之前打开的软件,回到之前的位置。所以Android 很有效的使用RAM,很多用户看到他们的RAM满了,就认为拖慢了他们的手机。而实际上,退出后重启这些程序才真正拖慢了手机的响应。而且这些自动杀进程的软件本身是个时刻活跃的进程,它始终在后台保持活跃使得CPU难以消停,反而增加了耗电量。 不用在意剩余内存的大小.其实很多人都是把使用其他系统的习惯带过来来了.安卓Android大多应用没有退出的设计其实是有道理的,这和系统对进程的调度机制有关系.如果你知道java,就能更清楚这机制了.其实和java的垃圾回收机制类似,系统有一个规则来回收内存.进行内存调度有个阀值,只有低于这个值系统才会按一个列表来关闭用户不需要的东西.当然这个值默认设置得很小,所以你会看到内存老在很少的数值徘徊.但事实上他并不影响速度.相反加快了下次启动应用的速度.这本来就是安卓Android标榜的优势之一,如果人为去关闭进程,没有太大必要.特别是自动关进程的软件. 到这里有人会说了,那为什么内存少的时候运行大型程序会慢呢?其实很简单,在内存剩余不多时打开大型程序,会触发系统自身的调进程调度策略,这是十分消耗系统资源的操作,特别是在一个程序频繁向系统申请内存的时候.这种情况下系统并不会关闭所有打开的进程,而是选择性关闭,频繁的调度自然会拖慢系统.所以,论坛上有个更改内存阀值的程序可以有一定改善. 但改动也可能带来一些问题,取决于值的设定. 那么,进程管理软件有无必要呢?有的.就是在运行大型程序之前,你可以手动关闭一些进程释放内存,可以显著的提高运行速度.但一些小程序,完全可交由系统自己管理.谈到这里,可能有的朋友会问,如果不关程序是不是会更耗电.我就说说安卓Android后台的原理,你就明白了.安卓Android 的应用在被切换到后台时,它其实已经被暂停了,并不会消耗cpu资源,只保留了运行状态.所以为什么有的程序切出去重进会到主界面.但是,一个程序如果想要在后台处理些东西,如音乐播放,它就会开启一个服务.服务可在后台持续运行,所以在后台耗电的也只有带服务的应用了.这个在进程管理软件里能看到,标签是service.所以没有带服务的应用在后台是完全不耗电的,没有必要关闭.这种设计本来就是一个非常好的设计,下次启动程序时,会更快,因为不需要读取界面资源,何必要关掉他们抹杀这个安卓Android的优点呢? 还有一个.为什么安卓Android一个应用看起来那么耗内存.大家知道,安卓Android上的应用是java,当然需要虚拟机,而安卓Android上的应用是带有独立虚拟机的,也就是每开一个应用就会打开一个独立的虚拟机.这样设计的原因是可以避免虚拟机崩溃导致整个系统崩溃,但代价就是需要更多内存. 以上这些设计确保了安卓Android的稳定性,正常情况下最多单个程序崩溃,但整个系统不会崩溃,也永远没有内存不足的提示出现.大家可能是被windows毒害得太深了,总想保留更多的内存,但实际上这并不一定会提升速度,相反却丧失了程序启动快的这一系统特色,很没必要.

JVM原理以及JVM内存管理机制

一、 JVM简介 JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。JVM工作原理和特点主要是指操作系统装入JVM是通过jdk中Java.exe来完成, 首先来说一下JVM工作原理中的jdk这个东西, .JVM 在整个jdk中处于最底层,负责于操作系统的交互,用来屏蔽操作系统环境,提供一个完整的Java运行环境,因此也就虚拟计算机. 操作系统装入JVM是通过jdk中Java.exe来完成。 通过下面4步来完成JVM环境. 1.创建JVM装载环境和配置 2.装载JVM.dll 3.初始化JVM.dll并挂界到JNIENV(JNI调用接口)实例 4.调用JNIEnv实例装载并处理class类。 对于JVM自身的物理结构,我们可以从下图了解:

JVM的一个重要的特征就是它的自动内存管理机制,在执行一段Java代码的时候,会把它所管理的内存划分 成几个不同的数据区域,其中包括: 1. 程序计数器,众所周知,JVM的多线程是通过线程轮流切换并 分配CPU执行时间的方式来实现的,那么每一个线程在切换 后都必须记住它所执行的字节码的行号,以便线程在得到CPU 时间时进行恢复,这个计数器用于记录正在执行的字节码指令的地址,这里要强调的是“字节码”,如果执行的是Native方法,那么这个计数器应该为null; 2.

3. Java计算栈,可以说整个Java程序的执行就是一个出栈入栈 的过程,JVM会为每一个线程创建一个计算栈,用于记录线程中方法的调用和变量的创建,由于在计算栈里分配的内存出栈后立即被抛弃,因此在计算栈里不存在垃圾回收,如果线程请求的栈深度大于JVM允许的深度,会抛出StackOverflowError 异常,在内存耗尽时会抛出OutOfMemoryError异常; 4. Native方法栈,JVM在调用操作系统本地方法的时候会使用到 这个栈; 5. Java堆,由于每个线程分配到的计算栈容量有限,对于可能会 占据大量内存的对象,则会被分配到Java堆中,在栈中包含了指向该对象内存的地址;对于一个Java程序来说,只有一个Java堆,也就是说,所有线程共享一个堆中的对象;由于Java堆不受线程的控制,如果在一个方法结束之后立即回收这个方法使用到的对象,并不能保证其他线程是否正在使用该对象;因此堆中对象的回收由JVM的垃圾收集器统一管理,和某一个线程无关;在HotSpot虚拟机中Java堆被划分为三代:o新生代,正常情况下新创建的对象会被分配到新生代,但如果对象占据的内存足够大以致超过了新生代的容量限 制,也可能被分配到老年代;新生代对象的一个特点是最 新、且生命周期不长,被回收的可能性高;

两种常见的内存管理方法:堆和内存池

两种常见的内存管理方法:堆和内存池 本文导读 在程序运行过程中,可能产生一些数据,例如,串口接收的数据,ADC采集的数据。若需将数据存储在内存中,以便进一步运算、处理,则应为其分配合适的内存空间,数据处理完毕后,再释放相应的内存空间。为了便于内存的分配和释放,AWorks提供了两种内存管理工具:堆和内存池。 本文为《面向AWorks框架和接口的编程(上)》第三部分软件篇——第9章内存管理——第1~2小节:堆管理器和内存池。 本章导读 在计算机系统中,数据一般存放在内存中,只有当数据需要参与运算时,才从内存中取出,交由CPU运算,运算结束再将结果存回内存中。这就需要系统为各类数据分配合适的内存空间。 一些数据需要的内存大小在编译前可以确定。主要有两类:一类是全局变量或静态变量,这部分数据在程序的整个生命周期均有效,在编译时就为这些数据分配了固定的内存空间,后续直接使用即可,无需额外的管理;一类是局部变量,这部分数据仅在当前作用域中有效(如函数中),它们需要的内存自动从栈中分配,也无需额外的管理,但需要注意的是,由于这一部分数据的内存从栈中分配,因此,需要确保应用程序有足够的栈空间,尽量避免定义内存占用较大的局部变量(比如:一个占用数K内存的数组),以避免栈溢出,栈溢出可能破坏系统关键数据,极有可能造成系统崩溃。 一些数据需要的内存大小需要在程序运行过程中根据实际情况确定,并不能在编译前确定。例如,可能临时需要1K内存空间用于存储远端通过串口发过来的数据。这就要求系统具有对内存空间进行动态管理的能力,在用户需要一段内存空间时,向系统申请,系统选择一段合适的内存空间分配给用户,用户使用完毕后,再释放回系统,以便系统将该段内存空间回收再利用。在AWorks中,提供了两种常见的内存管理方法:堆和内存池。9.1 堆管理器

Android 应用程序内存泄漏的分析

Android 应用程序内存泄漏的分析以前在学校里学习Java的时候,总是看到说,java是由垃圾收集器(GC)来管理内存回收的,所以当时形成的观念是Java不会产生内存泄漏,我们可以只管去申请内存,不需要关注内存回收,GC会帮我们完成。呵呵,很幼稚的想法,GC没那么聪明啊,理论及事实证明,我们的Java程序也是会有内存泄漏的。 (一)Java内存泄漏从何而来 一般来说内存泄漏有两种情况。一种情况如在C/C++语言中的,在堆中的分配的内存,没有将其释放,或者是在没有将其释放掉的时候,就将所有能访问这块内存的方式都删掉(如指针重新赋值);另一种情况则是在内存对象明明已经不需要的时候,还仍然保留着这块内存和它的访问方式(引用)。第一种情况,在Java中已经由于垃圾回收机制的引入,得到了很好的解决。所以,Java中的内存泄漏,主要指的是第二种情况。 (二)需要的工具 1.DDMS—Update heap Gause GC Heap 是DDMS自带的一个很不错的内存监控工具,下图红色框中最左边的图标就是该 工具的启动按钮,它能在Heap视图中显示选中进程的当前内存使用的详细情况。下图 框中最右边的是GC工具,很多时候我们使用Heap监控内存的时候要借助GC工具,点 击一次GC按钮就相当于向VM请求了一次GC操作。中间的按钮是Dump HPROF file,它 的功能相当于给内存拍一张照,然后将这些内存信息保存到hprof文件里面,在使用我 们的第二个工具MAT的时候会使用到这个功能。 2.MAT(Memory Analyzer Tool) Heap工具能给我们一个感性的认识,告诉我们程序当前的内存使用情况和是否存在内存 泄漏的肯能性。但是,如果我们想更详细,更深入的了解内存消耗的情况,找到问题所 在,那么我们还需要一个工具,就是MAT。这个工具是需要我们自己去下载的,可以下 载独立的MAT RCP 客户端,也可以以插件的形式安装到Eclipse里面,方便起见,推荐 后者。 安装方法: A.登录官网https://www.doczj.com/doc/2316025667.html,/mat/downloads.php B.下载MAT Eclipse插件安装包(红框所示,当然你也可是选择Update Site在线安装,个人觉得比较慢)

操作系统第五版答案第7章内存管理

第7章内存管理 复习题: 7.1.内存管理需要满足哪些需求? 答:重定位、保护、共享、逻辑组织和物理组织。 7.2.为什么需要重定位进程的能力? 答:通常情况下,并不能事先知道在某个程序执行期间会有哪个程序驻留在主存中。 此外还希望通过提供一个巨大的就绪进程池,能够把活动进程换入和换出主存,以便使处理器的利用率最大化。在这两种情况下,进程在主存中的确切位置是不可预知的。 7.3.为什么不可能在编译时实施内存保护? 答:由于程序在主存中的位置是不可预测的,因而在编译时不可能检查绝对地址来确保保护。并且,大多数程序设计语言允许在运行时进行地址的动态计算(例如,通过计算数组下标或数据结构中的指针)。因此,必须在运行时检查进程产生的所有存储器访问,以便确保它们只访问了分配给该进程的存储空间。 7.4.允许两个或多个进程访问进程的某一特定区域的原因是什么? 答:如果许多进程正在执行同一程序,则允许每个进程访问该程序的同一个副本要比让每个进程有自己单独的副本更有优势。同样,合作完成同一任务的进程可能需要共享访问同一个数据结构。 7.5.在固定分区方案中,使用大小不等的分区有什么好处? 答:通过使用大小不等的固定分区:1.可以在提供很多分区的同时提供一到两个非常大的分区。大的分区允许将很大的进程全部载入主存中。2.由于小的进程可以被放入小的分区中,从而减少了内部碎片。 7.6.内部碎片和外部碎片有什么区别? 答:内部碎片是指由于被装入的数据块小于分区大小而导致的分区内部所浪费的空间。外部碎片是与动态分区相关的一种现象,它是指在所有分区外的存储空间会变成越来越多的碎片的。 7.7.逻辑地址、相对地址和物理地址间有什么区别? 答:逻辑地址是指与当前数据在内存中的物理分配地址无关的访问地址,在执行对内存的访问之前必须把它转化成物理地址。相对地址是逻辑地址的一个特例,是相对于某些已知点(通常是程序的开始处)的存储单元。物理地址或绝对地址是数据在主存中的实际位置。 7.8.页和帧之间有什么区别? 答:在分页系统中,进程和磁盘上存储的数据被分成大小固定相等的小块,叫做页。 而主存被分成了同样大小的小块,叫做帧。一页恰好可以被装入一帧中。 7.9.页和段之间有什么区别? 答:分段是细分用户程序的另一种可选方案。采用分段技术,程序和相关的数据被划分成一组段。尽管有一个最大段长度,但并不需要所有的程序的所有段的长度都相等。习题: 7.1. 2.3节中列出了内存管理的5个目标,7.1节中列出了5中需求。请说明它们是一致 的。 答: 重定位≈支持模块化程序设计; 保护≈保护和访问控制以及进程隔离; 共享≈保护和访问控制; 逻辑组织≈支持模块化程序设计; 物理组织≈长期存储及自动分配和管理.

相关主题
文本预览
相关文档 最新文档