内存管理
百燕之家 / 信息详情
三星智能手机里面系统自带的内存管理器软件,怎样使用内存管理器软件里面自带的功能清理手机系统垃圾?
若要清除手机系统垃圾,建议您尝试以下操作:1.打开“应用程序”-点击“菜单”键-卸载不需要保留的软件。2.您可以自行下载第三方手机管理软件,进行清理操作。
Windows优化大师内存管理教程
Windows系统的内存管理并不总是十分有效。许多用户在刚启动系统时,觉得Windows的运行速度蛮快速的,可是过了一阵以后(例如,玩了一会儿大型的3D电子游戏退出后)就觉得整个系统的运行速度明显下降了。这是怎么回事呢?原来,由于物理内存总有上限,所以Windows系统采用了虚拟内存分页映射的机制管理内存,也就是分页系统通过分配部分硬盘作为附加内存。当被称为页的小块内存不再使用时,可以将它们存放到磁盘上去,以便腾出更多的物理内存空间。当Windows9x启动时,操作系统就要占掉差不多20多M物理内存,一旦运行别的程序,Windows就会不断在物理内存和虚拟内存之间交换内存页。NT可以使用多达64KB的页,但是Intel的CPU强制使用4KB的页。系统运行时间一长,内存中的页面碎片将越来越多,这就是系统运行速度下降的原因。 针对Windows操作系统内存管理的缺陷,Windows优化大师提供了内存整理功能。它适用于Windows9x/2000/XP/2003/Vista操作系统,能够在不影响系统速度的情况下有效的释放内存。在使用过程中,建议用户先进入Wopti 内存整理的设置界面,单击“推荐”后,选择“保存”返回软件主界面,这样将使Wopti 内存整理工作在最有效的状态。同时建议使用者在运行大型游戏软件时不要启动内存整理,大一点的游戏可能要吃掉大量的物理内存(通常都是100%),因此这时候整理和释放内存可能会降低游戏的速度。 快速释放。 针对深度整理的弊端,Windows优化大师内存整理模块为WindowsNT/2000/XP/2003/Vista用户提供了更为先进且独具特色的快速释放功能。它能在完全不影响系统速度的情况下,快速释放出物理内存。用户同时运行的应用程序越多,则效果越显著。快速整理后,应用程序的`运行速度不仅不会降低,反而因为内存安排更合理,速度会有所提高。先进的快速释放算法,不仅使快速释放速度极快(经测试,大多数情况下小于0.5秒即可完成),同时CPU占用率极低(经测试,大多数情况下整理过程中CPU占用率不超过5%)。快速释放虽然能够释放的内存空间较深度整理少一些,但由于每一段被释放的内存均经过精确计算,故效果更显著,建议Windows2000/XP/2003/Vista用户使用快速释放而摈弃深度整理。 深度整理。 Windows优化大师内存整理模块提供的深度整理与大多数内存释放软件一样,通过将物理内存中的内容移动到硬盘交换文件的虚拟内存中,从而为将要运行的应用程序提供更充裕的物理内存。深度整理能释放出最多的物理内存,但弊端也是明显的,由于不分青红皂白将物理内存进行强行迁移,导致用户正在运行的应用程序被迫重新进行物理内存与虚拟内存的数据交换,反而造成系统总体性能的下降。 深度整理过程中CPU占用率相对较低,并且整理过程中,用户可以点击“中止”按钮来中断内存深度整理释放操作。 在“深度整理”之前,弹出提示框,请您详细阅读,深度整理开始点击“确定” 整个深度整理过程,计算机运行将很不流畅,建议您这时不要使用任何程序,请耐心等待片刻。 点击“设置”后,将出现一个新的窗口,您可以根据实际情况进行设置,您也可以选择,系统启动时,自动运行“Wopti内存整理”,这样可以辅助您进行内存整理。 如果您无法准确判断,请点击“推荐”,Windows优化大师将为您做出最合适的选择。 点击“高级”,进入快速释放高级选项,根据您的实际情况进行选择,我们也给出了推荐项。完成点击“确定” 点击“关于”,是关于Wopti内存整理的版本说明。 注1:重复进行内存深度整理虽然用户将获得最大的物理内存,却会降低系统速度。因此,不建议连续多次深度整理内存。 注2:重复进行内存快速释放用户也有可能获得更多的物理内存,但相对深度整理已相当有限。 注3:深度整理前,Wopti 内存整理会自动监测并分析当前系统是否有能力完成内存碎片释放,若不具备此条件则不允许整理操作。例如:当用户的交换区即将耗尽时是不允许内存碎片整理的。
5.如何进行内存管理和自动释放
5.如何进行内存管理和自动释放?如何进行内存管理和自动释放?内存管理是计算机程序中必不可少的一环,因为程序在执行过程中需要不断地分配和释放内存资源。如果内存管理不当,会出现内存泄漏或溢出等问题,导致程序崩溃或造成其他不良后果。在iOS开发中,内存管理是一个特别重要的问题,因为iOS设备的内存资源相对较小,更容易出现内存不足的情况。为了提高程序的性能和稳定性,iOS开发者应该了解并掌握iOS中的内存管理技巧。以下是一些常用的内存管理和自动释放的技巧:1.使用ARC技术ARC全称为AutomaticReferenceCounting,是一种由编译器自动管理内存的技术。ARC能够自动计算对象的引用计数,当一个对象没有任何引用时,就会自动释放该对象所占用的内存。使用ARC可以减轻iOS开发者的内存管理负担,提高程序的效率和稳定性。2.避免循环引用循环引用是指两个或多个对象之间互相引用,导致它们的引用计数都不为零,从而无法正常释放。在iOS开发中,循环引用常常发生在使用Block、Delegate和通知等机制时。为了避免循环引用,可以使用weak、strong和copy等关键字来修饰对象,明确表达对象之间的拥有关系。3.使用单例模式单例模式是一种只允许创建一个对象的模式,可以有效地减少内存占用。在iOS开发中,单例模式广泛应用于管理全局数据和共享资源。单例对象可以在程序启动时创建并缓存在内存中,以便在需要时快速访问和更新。4.正确使用GCDGCD(GrandCentralDispatch)是一个强大的多线程框架,可以帮助iOS开发者轻松实现异步执行和线程安全。在使用GCD时,需要注意避免在主线程上执行耗时操作,否则会影响UI的响应和用户体验。同时,要及时释放GCD创建的队列和任务,以免造成内存泄漏。5.及时释放不必要的对象iOS开发者应该在程序执行过程中不断检查内存占用情况,及时释放不必要的对象和资源,以免占用过多的内存空间。为了方便内存管理和自动释放,可以使用@autoreleasepool和autorelease等关键字来明确表达对象的生命周期和释放策略。总之,在iOS开发中,良好的内存管理和自动释放是程序性能和稳定性的关键之一。iOS开发者应该认真学习和使用相关技术和工具,不断优化程序的内存管理和自动释放机制。
怎样卸载三星e7手机内存管理器
不管是设置手机上的那个程序或软件,只要是自带的,都是先获取到权限,在进行操作。使用应用宝软件对手机进行下root设置就行了。在电脑上安装个应用宝的软件,连接上要设置的手机就可以在电脑上使用工具对手机root了。到时候就可以将root之后,获取到的权限稳定的在手机上运行了。不管xiezai几个手机上自带的软件都是没有问题的了。
android之ION内存管理器(1)-- 简介
回顾2011年末[2],LWN审查了android kernel patch[3],以期望将这些patch合并到kernel主线中。但是PMEM(android实现的 一个内存分配器)使这个愿望破灭了。为什么PMEM不被linux 社区接受的原因在[3]中有讲到。从那开始,PMEM很明确会被完全抛弃,取而代之的是ION内存管理器。ION是google在Android4.0 ICS为了解决内存碎片管理而引入的通用内存管理器,它会更加融合kernel。目前QCOM MSM, NVDIA Tegra, TI OMAP, MRVL PXA都用ION替换PMEM。 http://android.googlesource.com/kernel/common.git ION codes reside in drivers/gpu/ion Specific usage examples on omap4: http://android.googlesource.com/kernel/omap.git ION 定义了四种不同的heap,实现不同的内存分配策略。 下图是两个client共享内存的示意图。图中有2个heap(每种heap都有自己的内存分配策略),每个heap中分配了若干个buffer。client的handle管理到对应的buffer。两个client是通过文件描述符fd来实现内存共享的。 定义了6种 ioctl 接口,可以与用户应用程序交互。 内核驱动也可以注册为一个ION的客户端(client),可以选择使用哪种类型的heap来申请内存。 Heap 接口定义 [drivers/gpu/ion/ion_priv.h] 这些接口不是暴露给驱动或者用户应用程序的。 ION 在/sys/kernel/debug/ion/ 提供一个debugfs 接口。 每个heap都有自己的debugfs目录,client内存使用状况显示在/sys/kernel/debug/ion/<<heap name>> 每个由pid标识的client也有一个debugfs目录/sys/kernel/debug/ion/<<pid>> 原文链接: https://blog.csdn.net/crazyjiang/article/details/7927260
iOS 管理内存的方式 - 内存管理系统
虚拟地址空间是指虚拟的、人们想象出来的地址空间,其实它并不存在,每个进程都有自己独立的虚拟空间,每个进程只能访问自己的地址空间,这样就能有效的做到了进程的 隔离 。 注: 虚拟储存的实现需要依赖硬件的支持,对于不同的CPU来说不同,但是几乎所有的硬件都采用MMU(Memory Management Unit)的部件来进行页映射。 把一段与程序所需要的内存空间大小的虚拟空间映射到某个地址空间。 当一个程序运行时,在某个时间段内,它只是频繁的用到了一小部分数据,程序的很多数据其实在一个时间段内都不会被用到。人们很自然的想到了更小粒度的内存分割和映射的方法,使得程序的局部性原理得到充分的利用,大大提高了内存的使用率。 原先的32位地址只能访问最多4GB的物理内存,但是自从扩展至36位地址线以后,Intel修改了页映射方式,使得新的映射方式可以访问到更多的物理内存,Intel把这个地址扩展方式叫做PAE(Physical Address Extension) 应用程序可以根据需求来选择申请和映射,比如一个应用程序0x10000000 ~0x20000000这一段256MB的虚拟地址空间用来做窗口,程序可以从高4GB的物理空间申请多个大小为256MB的物理空间,编号成A、B、C的等,然后根据需要将这个窗口映射到不同物理空间块,用到A时映射到A,用到B、C时再映射过去,叫做AWE(Address Windowing Extension),而Linux等UNIX类操作系统则采用mmap()系统调用来实现 一些存储在磁盘中的数据,在CPU执行这个地址指令时,发现页面是一个空的页面,于是他就认为这是一个 页错误 ,CPU将控制权交给操作系统,操作系统有专门处理例程来处理,操作系统将查询这个数据结构,然后找到空页面所在的VMA,计算相应的页面在可执行文件中的偏移,然后再物理内存中分配一个物理页面,将进程中该虚拟页与分配的物理页之间建立映射,然后再交给进程去执行。 ELF文件被映射时,是以页长度为单位的,每个段在映射时的长度应该是系统页长度的整倍数,如果不是,多余的部分页将占领一个页,造成了内存空间的大量浪费。而在ELF文件中,段的权限直邮为数不多的几种组合: 那么对于相同的段,我们把他们合并在一起当成一个段来映射,ELF可执行文件引入一个概念叫做Segment,一个segment包含一个或多个section,这样很明显的减少了页面内部的碎片,节省了空间 假设一个ELF执行文件,它有三个段需要装载,SEG0 、SEG1、SEG2,如图: 可以看到这种对齐方式在文件段的内部会有很多内部碎片,浪费磁盘空间,可执行文件总长度只有12014字节,却占了5个页。为了解决这个问题,UNIX系统采用了让那些个个段接壤的部分共用一个物理页面,将该物理页面映射两次,系统将它们映射两份到虚拟地址空间,其他的都按照正常的页粒度进行映射。
深入浅出iOS系统内核(3)— 内存管理
本文参考《Mac OS X and iOS Internals: To the Apple"s Core》 by Jonathan Levin 文章内容主要是阅读这本书的读书笔记,建议读者掌握《操作系统》,了解现代操作系统的技术特点,再阅读本文可以事半功倍。 虽然iOS系统内核使用极简的微内核架构,但内容依然十分庞大,所以会分 系统架构 、 进程调度 、 内存管理 和 文件系统 四个部分进行阐述。 操作系统管理所有的硬件资源,操作系统内核管理最核心的资源CPU和内存。上一篇阐述了Mach通过进程管理CPU,本文主要阐述XNU和Mach如何高效的管理内存 按照传统,栈一般都是保存自动变量,正常情况栈由系统管理,但是在iOS中某些情况下,程序员也可以选择用栈来动态分配内存,方法是使用鲜为人知的alloca( ) 这个函数的原型和malloc( )是一样的,区别在于这个函数返回的指针是栈上的地址而不是堆中的地址。 从实现角度,alloca( )从两方面优于malloc( ) 堆是由C语言运行时维护的用户态数据结构,通过堆的使用,程序可以不用直接在页面的层次处理内存分配。Darwin的libC 采用了一个基于分配区域(allocation zone)的特殊分配算法 在iOS中内存的管理是由在Mach层中进行的,BSD只是对Mach接口进行了POSIX封装,方便用户态进程调用。 XNU内存管理的核心机制是虚拟内存管理,在Mach 层中进行的,Mach 控制了分页器,并且向用户态导出了各种 vm_ 和 mach_vm_ 消息接口。 为方便用户态进程使用BSD对Mach 调用进行了封装,通过current_map( ) 获得当前的Mach 内存映射,最后再调用底层的Mach 函数。 BSD 的malloc 系列函数<bsd/sys/malloc.h> 头文件中。函数名为_MALLOC、_FREE、_REALLOC、_MALLOC_ZONE、_FREE_ZONE mcache机制是BSD 提供的基于缓存的非常高效的内存分配方法。默认实现基于mach zone,通过mach zone提供预分配好的缓存内存。 mcache具有可扩展架构,可以使用任何后端 slab 分配器。 使用mcache 机制的主要优点是速度:内存分配和维护是在每一个 CPU 自有的cache中进行的,因此可以映射到CPU的物理cache,从而极大地提升访问速度。 Mach VM层支持VM pressure 的机制,这个机制是可用RAM量低到危险程度的处置,下面我们会详细讲,这里不展开。 当RAM量低到危险时,Mach的pageout 守护程序会查询一个进程列表,查询驻留页面数,然后向驻留页面数最高的进程发送NOTE_VM_PRESSURE ,会在进程队列中发出一个事件。被选中的进程会响应这个压力通知,iOS中的运行时会调用 didReceiveMemoryWarning 方法。 然而有些时候这些操作没有效果,当内存压力机制失败之后,** 非常时间要用非常手段 **, Jetsam机制介入。 当进程不能通过释放内存缓解内存压力时,Jestam机制开始介入。这是iOS 实现的一个低内存清理的处理机制。也称为Memorystatus,这个机制有点类似于Linux的“Out-of-Memory”杀手,最初的目的就是杀掉消耗太多内存的进程。Memorystatus维护了两个列表: 在iOS的用户态可以通过 sysctl(2)查询这些列表,优先级列表可以在用户态进行设置。 在iOS 5中,Jestsam/Memorystatus 和默认的freezer 结合在一起,实现了对进程的冷冻而不是杀死。通过这种方式可以提供更好的用户体验,因为数据不会丢失,而且当内存情况好转时进程可以安全恢复。(感谢@易步指出本段错误) 用户态也可以通过pid_suspend( ) 和 pid_resume( )控制进程的休眠。 iOS 定义了 pid_hibernate,通过发送kern_hibernation_wakeup信号唤醒kernel_hibernation_thread 线程,这个线程专用于对进程冷冻操作。 实际的进程休眠操作是由jestsam_hibernate_top_proc 完成的,这个函数通过task_freeze冷冻底层的任务。 冷冻操作需要遍历任务的vm_map,然后将vm_map 传递给默认的 freezer。 VM是Darwin系统内存管理的核心机制。 VM 机制主要通过内存对象(memory object)和分页器(pager)的形式管理内存。 Mach 虚拟内存的实现非常全面而且通用。这部分由两个层次构成:一层是和硬件相关的部分,另一层构建在这一层之上和硬件无关的公共层。OS X 和 iOS 使用的几乎一样的底层机制,硬件无关层以及之上的BSD 层中的机制都是一样的。 Mach 的 VM子系统可以说是和其要管理的内存一样复杂和充满了各种细节。然后从高层次看,可以看到两个层次: 虚拟内存这一层完全以一种机器无关的方式来管理虚拟内存。这一层通过几种关键的抽象表示虚拟内存: Mach 允许使用多个分页器。事实上,默认就存在3~4个分页器。Mach 的分页器以外部实体的形式存在:是专业的任务,有点类似于其他系统上的内核交换(kernel-swapping)线程。Mach 的设计允许分页器和内核任务隔离开,设置允许用户态任务作为分页器。类似地,底层的后备存储也可以驻留在磁盘交换文件中(通过OS X 中的 default_pager 处理),可以映射到一个文件(由vnode_pager处理),可以是一个设备(由device_pager 处理)。注意:在Mach 中,每一个分页器处理的都是属于这个分页器的页面的请求,但是这些请求必须通过pageout 守护程序发出。这些守护程序(实际上就是内核线程)维护内核的页面表,并且判定哪些页面需要被清除出去。因此,这些守护程序维护的分页策略和分页器实现的分页操作是分开的。 物理内存的页面处理的是虚拟内存到物理内存的映射,因为虚拟内存中的内容最终总要存储在某个地方。这一层面只有一个抽象,那就是pmap,不过这个抽象非常重要,因为提供了机器无关的接口。这个接口隐藏了底层平台的细节,底层的细节需要在处理器层次进行分页操作,其中要处理的对象包括硬件页表项(page table entry,PTE)、翻译查找表(translation lookaside buffer,TLB)等。 每一个Mach 任务都要自己的虚拟内存空间,任务的struct task 中的 map 字段保存的就是这个虚拟内存空间。 vm_page_entry 中最关键的元素是vm_map_object,这是一个联合体,既可以包含另一个vm_map(作为子映射),也可以包含一个vm_object_t(由于这是一个联合体,所以具体的内容需要用布尔字段is_sub_map 来判断)。vm_object 是一个巨大的数据结构,其中包含了处理底层虚拟内存所需要的所有数据。vm_object的数据结构中的大部分字段都是用位表示的标志。这些字段表示了底层的内存状态(联动、物理连续和持久化等状态)和一些计数器(引用计数、驻留计数和联动计数等)。不过有3个字段需要特别注意: memq:vm_page 对象的链表,每一项都表示一个驻留内存的虚拟内存页面。尽管一个对象可以表示一个单独的页面,但是多数情况下一个对象可以包含多个页面,所以每一个页面关联到一个对象时都会有一个偏移值 page:memory_object 对象,这是指向分页器的Mach 端口。分页器将未驻留内存的页面关联到后备存储,后备存储可以是内存映射的文件、设备和交换文件,后备存储保存了没有驻留内存的页面。换句话说,分页器(可以有多个)负责将数据从后备存储移入内存以及将数据从内存移出到后备存储。分页器对于虚拟内存子系统来说极为重要 internal:vm_page 中众多标志位之一,如果这个位为真,那么表示这个对象是由内核内部使用的。这个标志位的值决定了对象中的页面会进入哪一个pageout队列 尽管内核和用户空间一样,基本上只在虚拟地址空间内操作,但是虚拟内存最终还是要翻译为物理地址的。机器的RAM 实际上是虚拟内存中开的窗口,允许程序访问虚拟内存是有限的,而且通常是不连续的区域,这些区域的上线就是机器上安装的内存。而虚拟内存中其他部分则要么延迟分配,要么共享,要么被交换到外部存储中,外部存储通常是磁盘。 然而虚拟内存和具体的底层架构相关。尽管虚拟内存和物理内存的概念在所有架构上本周都是一样的,但是具体的实现细节则各有千秋。XNU 构建与Mach 的物理内存抽象层之上,这个的抽象层成为pmap。pmap 从设计上对物理内存提供了一个统一的接口,屏蔽了架构相关的区别。这对于XNU来说非常有用,因为XNU支持的物理内存的架构包括以前的PowerPC,现在主要是Intel,然后在iOS 中还支持ARM。 Mach 的pmap 层逻辑上由一下两个子层构成: Mach Zone的概念相当于Linux的内存缓存(memory cache)和Windows 的Pool。Zone 是一种内存区域,用于快速分配和是否频繁使用的固定大小的对象。Zone的API是内核内部使用的,在用户态不能访问。Mach中Zone的使用非常广泛。 所有的zone 内存实际上都是在调用zinit( )时预先分配好的(zinit( )通过底层内存分配器kernel_memory_allocate( )分配内存)zalloc( )实际上是对REMOVE_FROM_ZONE 宏的封装,作用是返回zone的空闲列表中的下一个元素(如果zone已满,则调用kernel_memory_allocate( )分配这个zone在定义的alloc_size字节)。zfree( ) 使用的是相反功能的宏 ADD_TO_ZONE。这两个函数都会执行合理数量的参数检查,不过这些检查帮助不大:过去zone分配相关的bug已经导致了数据可以被黑客利用的内存损坏。zalloc( ) 最重要的客户是内核中的kalloc( ),这个函数从kalloc.*系列zone中分配内存。BSD的mcache机制也会从自己的zone中分配内存。BSD内核zone也是如此,BSD内核zone直接构建与Mach的zone之上。 进程的内存需求早晚会超过可用的RAM,系统必须有一种方法能够将不活动的页面备份起来,并且从RAM中删除,腾出更多的RAM给活动的页面使用,至少暂时能够满足活动页面的需求。在其他操作系统中,这个工作专门是由专门的内核线程完成的。在Mach 中,这些专门的任务称为分页器(pager),分页器可以是内核线程,设置建议是外部的用户态服务程序。 Mach分页器是一个内存管理器,负责将虚拟内存备份到某个特定类型的后备存储中。当内存容量不足,内存页面需要被交换出内存是,后备存储保存内存页面的内容:当换出的内存页面需要被使用时,将内存的页面恢复到RAM中。只有“脏”页面才需要进行上述的换出和换入,因为“脏”页面是在内存中修改过的页面,要从RAM中剔除时必须保存到磁盘中防止数据丢失。 要注意的是,这里提到的分页器仅仅实现了各自负责的内存对象的分页操作,这些分页器不会控制系统的分页策略。分页策略是有vm_pageout 守护线程负责的。 iOS 和 OS X 中XNU 包含的分页器种类都是一样的。下表是XNU中的内存分页器的多种类型: pageout 守护程序其实不是一个真的守护程序,而是一个线程。而且不是一般的线程:当kernel_bootstarp_thread( ) 完成内核初始化工作并且没有其他事情可做时,就调用vm_pageout( ) 成为了pageour 守护程序, vm_pageout( ) 永远不返回。这个线程管理页面交换的策略,判断哪些页面需要写回到其后备存储。 vm_pageout( ) 函数讲kernel_bootstrap_thread 线程转变为pageout 守护程序,这个函数实际上重新设置了这个线程。设置完成后,调用vm_pageout_continute( ),这个函数周期性地唤醒并执行vm_page_scan( ),维护4个页面表(称为页面队列)。系统中的每一个vm_page 都通过pageq字段绑定这4个队列中的一个: 垃圾回收线程(vm_pageout_garbage_collect( ))偶尔会被vm_pageout_scan( ) 通过其续体唤醒。垃圾回收机制线程处理4个方面的垃圾回收工作: vm_pageout( ) 守护程序处理的只是交换的一个方向,从物理内存换出到后备存储。而另外一个方向是页面换入,则是发生在页面错误的时候处理的。这个逻辑非常复杂,简化为一下步骤: 页错误有很多种,上述只是其中一种,其他类型的也错误还包括: VM系统是Mach中最重要最复杂而且最不好理解的子系统。Mach的内存管理核心是分页器,分页器允许将虚拟内存扩展到各种后背存储介质上:交换文件、内存映射文件、设备、甚至远程主机。 iOS中提高内存使用率的Freezer,以及处理内存耗尽的pageout守护程序。 https://www.amazon.com/Mac-OS-iOS-Internals-Apples/dp/1118057651/ref=sr_1_2?ie=UTF8&qid=1331298923&sr=8-2 https://developer.apple.com/library/content/documentation/Darwin/Conceptual/KernelProgramming/About/About.html
嵌入式操作系统中的内存管理功能有哪些
嵌入式系统所用到的内存管理机制主要有以下两种:1、虚拟内存管理机制:有一些嵌入式处理器提供了MMU,在MMU具备内存地址映射和寻址功能,它使操作系统的内存管理更加方便。如果存在MMU ,操作系统会使用它完成从虚拟地址到物理地址的转换, 所有的应用程序只需要使用虚拟地址寻址数据。 这种使用虚拟地址寻址整个系统的主存和辅存的方式在现代操作系统中被称为虚拟内存。MMU 便是实现虚拟内存的必要条件。虚拟内存的管理方法使系统既可以运行体积比物理内存还要大的应用程序,也可以实现“按需调页”策略,既满足了程序的运行速度,又节约了物理内存空间。 在Linux系统中,虚拟内存机制的实现实现为我们提供了一个典型的例子:在不同的体系结构下, 使用了三级或者两级页式管理,利用MMU 完成从虚拟地址到物理地址之间的转换。基于虚拟内存管理的内存最大好处是:由于不同进程有自己单独的进程空间,十分有效的提高了系统可靠性和安全性。2、非虚拟内存管理机制:在实时性要求比较高的情况下,很多嵌入式系统并不需要虚拟内存机制:因为虚拟内存机制会导致不确定性的 I/O阻塞时间, 使得程序运行时间不可预期,这是实时嵌入式系统的致命缺陷;另外,从嵌入式处理器的成本考虑,大多采用不装配MMU 的嵌入式微处理器。所以大多嵌入式系统采用的是实存储器管理策略。因而对于内存的访问是直接的,它对地址的访问不需要经过MMU,而是直接送到地址线上输出,所有程序中访问的地址都是实际的物理地址;而且,大多数嵌入式操作系统对内存空间没有保护,各个进程实际上共享一个运行空间。一个进程在执行前,系统必须为它分配足够的连续地址空间,然后全部载入主存储器的连续空间。由此可见,嵌入式系统的开发人员不得不参与系统的内存管理。从编译内核开始,开发人员必须告诉系统这块开发板到底拥有多少内存;在开发应用程序时,必须考虑内存的分配情况并关注应用程序需要运行空间的大小。另外,由于采用实存储器管理策略,用户程序同内核以及其它用户程序在一个地址空间,程序开发时要保证不侵犯其它程序的地址空间,以使得程序不至于破坏系统的正常工作,或导致其它程序的运行异常;因而,嵌入式系统的开发人员对软件中的一些内存操作要格外小心。UCOS就是使用非虚拟内存管理的一个例子,在UCOS中,所有的任务共享所有的物理内存,任务之间没有内存保护机制,这样能够提高系统的相应时间,但是任务内存操作不当,会引起系统崩溃。段式存储管理和页式存储管理的本质区别:页式存储管理中的逻辑地址有页号和业内地址两部分组成,但作业仍然使用连续的逻辑地址,可把它看作是一维的(线性的)地址结构。用户没有分页的概念,操作系统把作业信息装入主存时才按照块长进行分页。段式存储管理中的逻辑地址有段号和段内地址两部分组成。他支持用户的分段,每段内的逻辑地址是连续的,而段与段之间的逻辑地址是不连续的。因此段式存储管理中的逻辑地址实际上是采用了二维的地址结构。
Android与iOS的内存管理机制的区别,在网上看的一些文章说linux和unix是一样的呢,那手机上有什么不一样
Android清理内存时候,它不会直接关掉App,而是筛选出其中的一些无用对象,率先把它们关闭掉,从而释放部分内存。但只要你不手动关,这些后台程序都不会自动消失。所以就算你今天上午在微信和朋友聊天,晚上再从后台调出微信,画面也肯定还停留在那时候iOS会无差别地关掉老应用,这意味着如果你正在玩一个大型游戏,那么可能微信、微博、QQ这些软件就全都被自动强行关掉了。你若想切换回这些软件,就要经历一个相当闹心的启动过程。另外一个比较闹心的场景就是你在Safari浏览器里开了N个标签,1GB运存承受不住那么多的内容,于是非常自觉地把最老的几个网页给挂起了。如果那几个页面刚好是你填了一半的付款界面或者是写了一半的文章,那么就全没了
进程内存管理方法?
Linux系统提供了复杂的存储管理系统,使得进程所能访问的内存达到4GB。在Linux系统中,进程的4GB内存空间被分为两个部分——用户空间与内核空间。用户空间的地址一般分布为0~3GB(即PAGE_OFFSET,在Ox86中它等于OxC0000000),这样,剩下的3~4GB为内核空间,用户进程通常只能访问用户空间的虚拟地址,不能访问内核空间的虚拟地址。用户进程只有通过系统调用(代表用户进程在内核态执行)等方式才可以访问到内核空间。每个进程的用户空间都是完全独立、互不相干的,用户进程各自有不同的页表。而内核空间是由内核负责映射,它并不会跟着进程改变,是固定的。内核空间的虚拟地址到物理地址映射是被所有进程共享的,内核的虚拟空间独立于其他程序。Linux中1GB的内核地址空间又被划分为物理内存映射区、虚拟内存分配区、高端页面映射区、专用页面映射区和系统保留映射区这几个区域。对于x86系统而言,一般情况下,物理内存映射区最大长度为896MB,系统的物理内存被顺序映射在内核空间的这个区域中。当系统物理内存大于896MB时,超过物理内存映射区的那部分内存称为高端内存(而未超过物理内存映射区的内存通常被称为常规内存),内核在存取高端内存时必须将它们映射到高端页面映射区。Linux保留内核空间最顶部FIXADDR_TOP~4GB的区域作为保留区。当系统物理内存超过4GB时,必须使用CPU的扩展分页(PAE)模式所提供的64位页目录项才能存取到4GB以上的物理内存,这需要CPU的支持。加入了PAE功能的Intel Pentium Pro及以后的CPU允许内存最大可配置到64GB,它们具备36位物理地址空间寻址能力。由此可见,对于32位的x86而言,在3~4GB之间的内核空间中,从低地址到高地址依次为:物理内存映射区隔离带vmalloc虚拟内存分配器区隔离带高端内存映射区专用页面映射区保留区。
Android系统内存管理
部分内容出至林学森的Android内核设计思想。 Android官网内存管理 部分出至 https://www.jianshu.com/p/94d1cd553c44 Android本质是Linux所以先从Linux说起。 Linux的内存管理为系统中所有的task提供可靠的内存分配、释放和保护机制。 核心: 虚拟内存 内存分配与释放 内存保护 将外存储器的部分空间作为内存的扩展,如从硬盘划出4GB大小。 当内存资源不足时,系统按照一定算法自动条形优先级低的数据块,并把他们存储到硬盘中。 后续如果需要用到硬盘中的这些数据块,系统将产生“缺页”指令,然后把他们交换回内存中。 这些都是由操作系统内核自动完成的,对上层应用”完全透明“。 每个进程的逻辑地址和物理地址都不是直接对应的,任何进程都没办法访问到它管辖范围外的内存空间——即刻意产生的内存越界与非法访问,操作系统也会马上阻止并强行关闭程序,从而有力的保障应用程序和操作系统的安全和稳定。 一旦发现系统的可用内存达到临界值,机会按照优先级顺序,匆匆低到高逐步杀掉进程,回收内存。 存储位置:/proc/<PID>/oom_score 优先级策略: 进程消耗的内存 进程占用的CPU时间 oom_adj(OOM权重) Android平台运行的前提是可用内存是浪费的内存。它试图在任何时候使用所有可用的内存。例如,系统会在APP关闭后将其保存在内存中,以便用户可以快速切换回它们。出于这个原因,Android设备通常运行时只有很少的空闲内存。在重要系统进程和许多用户应用程序之间正确分配内存内对存管理是至关重要。 Android有两种主要的机制来处理低内存的情况:内核交换守护进程(kernel swap daemon)和低内存杀手(low-memory killer)。 当用户在APP之间切换时,Android会在最近使用的(LRU)缓存中保留不在前台的APP,即用户看不到的APP,或运行类似音乐播放的前台服务。如果用户稍后返回APP,系统将重用该进程,从而使APP切换更快。 如果你的APP有一个缓存进程,并且它保留了当前不需要的内存,那么即使用户不使用它,你的APP也会影响系统的整体性能。由于系统内存不足,它会从最近使用最少的进程开始杀死LRU缓存中的进程。该系统还负责处理占用最多内存的进程,并可以终止这些进程以释放RAM。 当系统开始终止LRU缓存中的进程时,它主要是自底向上工作的。系统还考虑哪些进程消耗更多的内存,从而在终止时为系统提供更多的内存增益。你在LRU列表中消耗的内存越少,你就越有可能留在列表中并能够快速恢复。 为了满足RAM的所有需求,Android尝试共享RAM来跨进程通信。它可以做到以下方式: Android设备包含三种不同类型的内存:RAM、zRAM和storage。 注意:CPU和GPU都访问同一个RAM。 内存被拆分成页。通常每页有4KB的内存。 页面被认为是空闲的或已使用的。 空闲页是未使用的RAM。 已使用页是系统正在积极使用的RAM,分为以下类别: 干净的页面(Clean pages)包含一个文件(或文件的一部分)的一份精确副本存在存储器上。当一个干净的页面不再包含一个精确的文件副本(例如,来自应用程序操作的结果)时,它就变成了脏页。可以删除干净的页,因为它们始终可以使用存储中的数据重新生成;不能删除脏页(Dirty pages),否则数据将丢失。 内核跟踪系统中的所有内存页。 当确定一个应用程序正在使用多少内存时,系统必须考虑shared pages。APP访问相同的服务或库将可能共享内存页。例如,Google Play Services 和一个游戏APP可能共享一个位置服务。这使得很难确定有多少内存属于这个服务相对于每个APP。 当操作系统想要知道所有进程使用了多少内存时,PSS非常有用,因为页面不会被多次计数。PSS需要很长时间来计算,因为系统需要确定哪些页面是共享的,以及被有多少进程。RSS不区分共享页面和非共享页面(使计算速度更快),更适合于跟踪内存分配的更改。 内核交换守护进程(kswapd)是Linux内核的一部分,它将使用过的内存转换为空闲内存。当设备上的空闲内存不足时,守护进程将变为活动状态。Linux内核保持低和高的可用内存阈值。当空闲内存低于低阈值时,kswapd开始回收内存。当空闲内存达到高阈值,kswapd将停止回收内存。 kswapd可以通过删除干净的页面来回收干净的页面,因为它们有存储器支持并且没有被修改。如果进程试图寻址已删除的干净页,则系统会将该页从存储器复制到RAM。此操作称为请求分页。 kswapd将缓存的私有脏页(private dirty pages)和匿名脏页(anonymous dirty pages)移动到zRAM进行压缩。这样做可以释放RAM中的可用内存(空闲页)。如果进程试图触摸zRAM中脏页,则该页将被解压缩并移回RAM。如果与压缩页关联的进程被终止,则该页将从zRAM中删除。 如果可用内存量低于某个阈值,系统将开始终止进程。 lmkd实现源码要在system/core/lmkd/lmkd.c。 lmkd会创建名为lmkd的socket,节点位于/dev/socket/lmkd,该socket用于跟上层framework交互。 小结: LMK_TARGET: AMS.updateConfiguration() 的过程中调用 updateOomLevels() 方法, 分别向/sys/module/lowmemorykiller/parameters目录下的minfree和adj节点写入相应信息; LMK_PROCPRIO: AMS.applyOomAdjLocked() 的过程中调用 setOomAdj() 向/proc/<pid>/oom_score_adj写入oom_score_adj后直接返回; LMK_PROCREMOVE: AMS.handleAppDiedLocked 或者 AMS.cleanUpApplicationRecordLocked() 的过程,调用remove(),目前不做任何事,直接返回; 为了进一步帮助平衡系统内存并避免终止APP进程,可以Activity类中实现ComponentCallbacks2接口。提供的onTrimMemory()回调方法允许APP在前台或后台侦听与内存相关的事件,然后释放对象以响应应用程序生命周期或表明系统需要回收内存的系统事件。 onTrimMemory()回调是在Android 4.0(API级别14)中添加的。 对于早期版本,可以使用onLowMemory(),它大致相当于TRIM_MEMORY_COMPLETE事件。 一个专门的驱动。(Linux Kernel 4.12 已移除交给kswapd处理)。 很多时候,kswapd无法为系统释放足够的内存。在这种情况下,系统使用onTrimMemory()通知APP内存不足,应该减少其分配。如果这还不够,内核将开始终止进程以释放内存,它使用低内存杀手(LMK)来完成这个任务。 为了决定要终止哪个进程,LMK使用一个名为oom_adj_score的“out of memory”分数来确定运行进程的优先级,高分的进程首先被终止。 后台应用程序首先被终止,系统进程最后被终止。 下表列出了从高到低的LMK评分类别。第一排得分最高的项目将首先被杀死: Android Runtime(ART)和Dalvik虚拟机使用分页(Paging)和内存映射(mmapping)来管理内存。应用程序通过分配新对象或触摸已映射页面来修改内存都将保留在RAM中,并且不能被调出。应用程序释放内存的唯一方式是垃圾收集器。
常见的嵌入式OS内存管理和进程调度方式
任务调度 :抢占式实时内核,即优先级最高的任务优先运行,不论什么时候,只要就绪的任务中有比当前正在执行的任务优先级更高的任务,就暂停当前的任务去执行优先级最高的任务(固定优先级抢占式)。UCOS-II中共有64个任务,分为8组,每8个为一组。 内存管理 :μC/OS-II提供的内存管理机制是把连续的大块内存按分区来管理,每个分区中包含整数个大小相同的内存块,不同分区的大小可不相同,同一个分区的大小相同。因申请的空间大小固定,故即使是频繁地申请和释放内存也不会产生内存碎片问题,但其缺点是内存的利用率相对不高。 任务调度 :FreeRTOS内核既支持优先级调度算法,同时支持轮换调度算法。同时,支持用户自定义可剥夺和不可剥夺。当进行任务调度时,调度算法首先进行优先级调度。系统按照优先级从高到低的顺序从就绪任务链表数组中寻找第一个最高就绪优先级,据此实现优先级调度。若此优先级下只有一个就绪任务,则此就绪任务进入运行态;若此优先级下有多个就绪任务,则需采用轮换调度算法实现多任务轮流执行。 内存管理 :提供5种内存管理机制,分别放置于heap_*.c文件中。 内存管理 :继承了标准linux的管理方式,但因为缺少MMU(内存管理单元)导致对内存的操作是对真实地址进行,无法做到程序隔离。 任务调度 :uClinux在结构上继承了标准Linux的多任务实现方式(分实时进程和普通进程采用不同的调度策略、即先来的先服务调度和时间片轮转调度) 进程调度 :支持两种进程调度,位图调度(一个任务一个优先级)、多队列调度(一个优先级可对应多个任务) 内存管理 :ecos提供了两种内存池:一种是变长内存池(variable size memory pool),根据申请的大小进行分配;另一种是定长内存池(fixed size memory pool),以固定大小的块为单位进行分配。变长内存池使用链表来进行管理,定长内存池使用位图来进行管理。C库函数malloc使用变长内存池实现内存分配,用户可以直接使用C库函数malloc和free管理内存。
LINUX系统的内存管理知识详解
内存是Linux内核所管理的最重要的资源之一。内存管理系统是操作系统中最为重要的部分,因为系统的物理内存总是少于系统所需要的内存数量。虚拟内存就是为了克服这个矛盾而采用的策略。系统的虚拟内存通过在各个进程之间共享内存而使系统看起来有多于实际内存的内存容量。Linux支持虚拟内存, 就是使用磁盘作为RAM的扩展,使可用内存相应地有效扩大。核心把当前不用的内存块存到硬盘,腾出内存给其他目的。当原来的内容又要使用时,再读回内存。以下就是我为大家整理到的详细LINUX系统内存管理的知识,欢迎大家阅读!!! LINUX系统教程:内存管理的知识详解 一、内存使用情况监测 (1)实时监控内存使用情况 在命令行使用“Free”命令可以监控内存使用情况 代码如下: #free total used free shared buffers cached Mem: 256024 192284 63740 0 10676 101004 -/+ buffers/cache: 80604 175420 Swap: 522072 0 522072 上面给出了一个256兆的RAM和512兆交换空间的系统情况。第三行输出(Mem:)显示物理内存。total列不显示核心使用的物理内存(通常大约1MB)。used列显示被使用的内存总额(第二行不计缓冲)。 free列显示全部没使用的内存。Shared列显示多个进程共享的内存总额。Buffers列显示磁盘缓存的当前大小。第五行(Swap:)对对换空间,显示的信息类似上面。如果这行为全0,那么没使用对换空间。在缺省的状态下,free命令以千字节(也就是1024字节为单位)来显示内存使用情况。可以使用—h参数以字节为单位显示内存使用情况,或者可以使用—m参数以兆字节为单位显示内存使用情况。还可以通过—s参数使用命令来不间断地监视内存使用情况: #free –b –s2 这个命令将会在终端窗口中连续不断地报告内存的使用情况,每2秒钟更新一次。 (2)组合watch与 free命令用来实时监控内存使用情况: 代码如下: #watch -n 2 -d free Every 2.0s: free Fri Jul 6 06:06:12 2007 total used free shared buffers cached Mem: 233356 218616 14740 0 5560 64784 -/+ buffers/cache: 148272 85084 Swap: 622584 6656 615928 watch命令会每两秒执行 free一次,执行前会清除屏幕,在同样位置显示数据。因为 watch命令不会卷动屏幕,所以适合出长时间的监测内存使用率。可以使用 -n选项,控制执行的频率;也可以利用 -d选项,让命令将每次不同的地方显示出来。Watch命令会一直执行,直到您按下 [Ctrl]-[C] 为止。 二、虚拟内存的概念 (1)Linux虚拟内存实现机制 Linux虚拟内存的实现需要六种机制的支持:地址映射机制、内存分配回收机制、缓存和刷新机制、请求页机制、交换机制、内存共享机制。 首先内存管理程序通过映射机制把用户程序的逻辑地址映射到物理地址,在用户程序运行时如果发现程序中要用的虚地址没有对应的物理内存时,就发出了请求页要求;如果有空闲的内存可供分配,就请求分配内存(于是用到了内存的分配和回收),并把正在使用的物理页记录在缓存中(使用了缓存机制)。 如果没有足够的内存可供分配,那么就调用交换机制,腾出一部分内存。另外在地址映射中要通过TLB(翻译后援存储器)来寻找物理页;交换机制中也要用到交换缓存,并且把物理页内容交换到交换文件中后也要修改页表来映射文件地址。 (2)虚拟内存容量设定 也许有人告诉你,应该分配2倍于物理内存的虚拟内存,但这是个不固定的规律。如果你的物理保存比较小,可以这样设定。如果你有1G物理内存或更多的话,可以缩小一下虚拟内存。Linux会把大量的内存用做Cache的,但在资源紧张时回收回.。你只要看到swap为0或者很小就可以放心了,因为内存放着不用才是最大的浪费。 三、使甩vmstat命令监视虚拟内存使用情况 vmstat是Virtual Meomory Statistics(虚拟内存统计)的缩写,可对操作系统的虚拟内存、进程、CPU活动进行监视。它是对系统的整体情况进行统计,不足之处是无法对某个进程进行深入分析。通常使用vmstat 5 5(表示在5秒时间内进行5次采样)命令测试。将得到一个数据汇总它可以反映真正的系统情况。 代码如下: #vmstat 5 5 procs -----------memory---------- ---swap-- -----io---- --system-- ----cpu---- r b swpd free buff cache si so bi bo in cs us sy id wa 1 0 62792 3460 9116 88092 6 30 189 89 1061 569 17 28 54 2 0 0 62792 3400 9124 88092 0 0 0 14 884 434 4 14 81 0 0 0 62792 3400 9132 88092 0 0 0 14 877 424 4 15 81 0 1 0 62792 3400 9140 88092 0 0 0 14 868 418 6 20 74 0 1 0 62792 3400 9148 88092 0 0 0 15 847 400 9 25 67 0 vmstat命令输出分成六个部分: (1)进程procs: r:在运行队列中等待的进程数 。 b:在等待io的进程数 。 (2)内存memoy: swpd:现时可用的交换内存(单位KB)。 free:空闲的内存(单位KB)。 buff: 缓冲去中的内存数(单位:KB)。 cache:被用来做为高速缓存的内存数(单位:KB)。 (3) swap交换页面 si: 从磁盘交换到内存的交换页数量,单位:KB/秒。 so: 从内存交换到磁盘的交换页数量,单位:KB/秒。 (4) io块设备: bi: 发送到块设备的块数,单位:块/秒。 bo: 从块设备接收到的块数,单位:块/秒。 (5)system系统: in: 每秒的中断数,包括时钟中断。 cs: 每秒的环境(上下文)切换次数。 (6)cpu中央处理器: cs:用户进程使用的时间 。以百分比表示。 sy:系统进程使用的时间。 以百分比表示。 id:中央处理器的空闲时间 。以百分比表示。 如果 r经常大于 4 ,且id经常小于40,表示中央处理器的负荷很重。 如果bi,bo 长期不等于0,表示物理内存容量太小。 四、Linux 服务器的内存泄露和回收内存的方法 1、内存泄漏的定义: 一般我们常说的内存泄漏是指堆内存的泄漏。堆内存是指程序从堆中分配的,大小任意的(内存块的大小可以在程序运行期决定),使用完后必须显示释放的内存。应用程序一般使用malloc,realloc,new等函数从堆中分配到一块内存,使用完后,程序必须负责相应的调用free或释放该内存块,否则,这块内存就不能被再次使用,我们就说这块内存泄漏了。 2、内存泄露的危害 从用户使用程序的角度来看,内存泄漏本身不会产生什么危害,作为一般的用户,根本感觉不到内存泄漏的存在。真正有危害的`是内存泄漏的堆积,这会最终消耗尽系统所有的内存。从这个角度来说,一次性内存泄漏并没有什么危害,因为它不会堆积,而隐式内存泄漏危害性则非常大,因为较之于常发性和偶发性内存泄漏它更难被检测到。存在内存泄漏问题的程序除了会占用更多的内存外,还会使程序的性能急剧下降。对于服务器而言,如果出现这种情况,即使系统不崩溃,也会严重影响使用。 3、内存泄露的检测和回收 对于内存溢出之类的麻烦可能大家在编写指针比较多的复杂的程序的时候就会遇到。在 Linux 或者 unix 下,C、C++语言是最使用工具。但是我们的 C++ 程序缺乏相应的手段来检测内存信息,而只能使用 top 指令观察进程的动态内存总额。而且程序退出时,我们无法获知任何内存泄漏信息。 使用kill命令 使用Linux命令回收内存,我们可以使用Ps、Kill两个命令检测内存使用情况和进行回收。在使用超级用户权限时使用命令“Ps”,它会列出所有正在运行的程序名称,和对应的进程号(PID)。Kill命令的工作原理是:向Linux操作系统的内核送出一个系统操作信号和程序的进程号(PID)。 应用例子: 为了高效率回收内存可以使用命令ps 参数v: 代码如下: [root@www ~]# ps v PID TTY STAT TIME MAJFL TRS DRS RSS %MEM COMMAND 2542 tty1 Ss+ 0:00 0 8 1627 428 0.1 /sbin/mingetty tty1 2543 tty2 Ss+ 0:00 0 8 1631 428 0.1 /sbin/mingetty tty2 2547 tty3 Ss+ 0:00 0 8 1631 432 0.1 /sbin/mingetty tty3 2548 tty4 Ss+ 0:00 0 8 1627 428 0.1 /sbin/mingetty tty4 2574 tty5 Ss+ 0:00 0 8 1631 432 0.1 /sbin/mingetty tty5 2587 tty6 Ss+ 0:00 0 8 1627 424 0.1 /sbin/mingetty tty6 2657 tty7 Ss+ 1:18 12 1710 29981 7040 3.0 /usr/bin/Xorg :0 -br -a 2670 pts/2 Ss 0:01 2 682 6213 1496 0.6 -bash 3008 pts/4 Ss 0:00 2 682 6221 1472 0.6 /bin/bash 3029 pts/4 S+ 0:00 2 32 1783 548 0.2 ping 192.168.1.12 3030 pts/2 R+ 0:00 2 73 5134 768 0.3 ps v 然后如果想回收Ping命令的内存的话,使用命令: 代码如下: # Kill -9 3029 使用工具软件 Memprof是一个非常具有吸引力且非常易于使用的软件,它由Red Hat的Owen Talyor创立。这个工具是用于GNOME前端的Boehm-Demers-Weiser垃圾回收器。这个工具直接就可以执行,并且其工作起来无需对源代码进行任何修改。在程序执行时,这个工具会以图形化的方式显示内存的使用情况。 相关介绍:Linux 严格来讲,Linux这个词本身只表示Linux内核,但人们已经习惯了用Linux来形容整个基于Linux内核,并且使用GNU 工程各种工具和数据库的操作系统。 Linux拥有以下特性:类似于Unix的基本思想,支持完全免费与自由传播,完全兼容POSIX1.0标准,支持多用户、多任务、有着良好的界面、支持多种平台。Linux 能运行主要的UNIX工具软件、应用程序和网络协议。它支持32位和64位硬件。Linux继承了Unix以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。 Linux有着许多不同的版本,但它们都使用了Linux内核。Linux可安装在各种计算机硬件设备中,比如手机、平板电脑、路由器、视频游戏控制台、台式计算机、大型机和超级计算机。
Linux内存管理内存检测技术(slub_debug/kmemleak/kasan) [超详细]
F:在free的时候会执行检查。 Z:表示Red Zone的意思。 P:是Poison的意思。 U:会记录slab的使用者信息,如果打开,会会显示分配释放对象的栈回溯。 Redzone overwritten Object padding overwritten Object already free Poison overwritten slab-out-of-bounds user-after-free 测试结果如下: stack-out-of-bounds global-out-of-bounds 测试结果如下:
Linux 内核的内存管理 - 概念
Concepts overview — The Linux Kernel documentation Linux中的内存管理是一个复杂的系统,经过多年的发展,它包含越来越多的功能,以支持从 MMU-less microcontrollers 到 supercomputers 的各种系统。 没有MMU内存管理的系统被称为 nommu ,它值得写一份专门的文档进行描述。 尽管有些概念是相同的,这里我们假设MMU可用,CPU可以将虚拟地址转换为物理地址。 计算机系统中的物理内存是有限资源,即便支持内存热插拔,其可以安装的内存也有限的。物理内存不一定必须是连续的;它可以作为一组不同的地址范围被访问。此外,不同的CPU架构,甚至同架构的不同实现对如何定义这些地址范围都是不同的。 这使得直接处理物理内存异常复杂,为了避免这种复杂性,开发了 虚拟内存 (virtual memory) 的概念。 虚拟内存从应用软件中抽象出物理内存的细节,只允许在物理内存中保留需要的信息 (demand paging) ,并提供一种机制来保护和控制进程之间的数据共享。 通过虚拟内存,每次内存访问都访问一个 虚拟地址 。当CPU对从系统内存读取(或写入)的指令进行解码时,它将该指令中编码的虚拟地址转换为内存控制器可以理解的物理地址。 物理内存被切分为 页帧 page frames 或 页 pages 。页的大小是基于架构的。一些架构允许从几个支持的值中选择页大小;此选择在内核编译时设置到内核配置。 每个物理内存页都可以映射为一个或多个 虚拟页(virtual pages) 。映射关系描述在 页表(page tables) 中,页表将程序使用的虚拟地址转换为物理内存地址。页表以层次结构组织。 最底层的表包含软件使用的实际内存页的物理地址。较高层的表包含较低层表页的物理地址。顶层表的指针驻留在寄存器中。 当CPU进行地址转换的时候,它使用寄存器访问顶级页表。 虚拟地址的高位,用于顶级页表的条目索引。然后,通过该条目访问下级,下级的虚拟地址位又作为其下下级页表的索引。虚拟地址的最低位定义实际页内的偏移量。 地址转换需要多次内存访问,而内存访问相对于CPU速度来说比较慢。为了避免在地址转换上花费宝贵的处理器周期,CPU维护着一个称为 TLB (Translation Lookaside Buffer)的用于地址转换缓存(cache)。通常TLB是非常稀缺的资源,需要大内存工作应用程序会因为TLB未命中而影响性能。 很多现代CPU架构允许页表的高层直接映射到内存页。例如,x86架构,可以通过二级、三级页表的条目映射2M甚至1G内存页。在Linux中,这些内存页称为 大页 (Huge) 。大页的使用显著降低了TLB的压力,提高了TLB命中率,从而提高了系统的整体性能。 Linux提供两种机制开启使用大页映射物理内存。 第一个是 HugeTLB 文件系统,即 hugetlbfs 。它是一个伪文件系统,使用RAM作为其存储。在此文件系统中创建的文件,数据驻留在内存中,并使用大页进行映射。 关于 HugeTLB Pages 另一个被称为 THP (Transparent HugePages) ,后出的开启大页映射物理内存的机制。 与 hugetlbfs 不同,hugetlbfs要求用户和/或系统管理员配置系统内存的哪些部分应该并可以被大页映射;THP透明地管理这些映射并获取名称。 关于 Transparent Hugepage Support 通常,硬件对不同物理内存范围的访问方式有所限制。某些情况下,设备不能对所有可寻址内存执行DMA。在其他情况下,物理内存的大小超过虚拟内存的最大可寻址大小,需要采取特殊措施来访问部分内存。还有些情况,物理内存的尺寸超过了虚拟内存的最大可寻址尺寸,需要采取特殊措施来访问部分内存。 Linux根据内存页的使用情况,将其组合为多个 zones 。比如, ZONE_DMA 包含设备用于DMA的内存, ZONE_HIGHMEM 包含未永久映射到内核地址空间的内存, ZONE_NORMAL 包含正常寻址内存页。 内存zones的实际层次架构取决于硬件,因为并非所有架构都定义了所有的zones,不同平台对DMA的要求也不同。 多处理器机器很多基于 NUMA (Non-Uniform Memory Access system - 非统一内存访问系统 )架构。 在这样的系统中,根据与处理器的“距离”,内存被安排成具有不同访问延迟的 banks 。每个 bank 被称为一个 node ,Linux为每个 node 构造一个独立的内存管理子系统。 Node 有自己的zones集合、free&used页面列表,以及各种统计计数器。 What is NUMA? NUMA Memory Policy 物理内存易失,将数据放入内存的常见情况是读取文件。读取文件时,数据会放入 页面缓存(page cache) ,可以在再次读取时避免耗时的磁盘访问。同样,写文件时,数据也会被放入 页面缓存 ,并最终进入存储设备。被写入的页被标记为 脏页(dirty page) ,当Linux决定将其重用时,它会将更新的数据同步到设备上的文件。 匿名内存 anonymous memory 或 匿名映射 anonymous mappings 表示没有后置文件系统的内存。这些映射是为程序的stack和heap隐式创建的,或调用mmap(2)显式创建的。通常,匿名映射只定义允许程序访问的虚拟内存区域。读,会创建一个页表条目,该条目引用一个填充有零的特殊物理页。写,则分配一个常规物理页来保存写入数据。该页将被标记为脏页,如果内核决定重用该页,则脏页将被交换出去 swapped out 。 纵贯整个系统生命周期,物理页可用于存储不同类型的数据。它可以是内核内部数据结构、设备驱动DMA缓冲区、读取自文件系统的数据、用户空间进程分配的内存等。 根据内存页使用情况,Linux内存管理会区别处理。可以随时释放的页面称为 可回收(reclaimable) 页面,因为它们把数据缓存到了其他地方(比如,硬盘),或者被swap out到硬盘上。 可回收页最值得注意的是 页面缓存 和 匿名页面 。 在大多数情况下,存放内部内核数据的页,和用作DMA缓冲区的页无法重用,它们将保持现状直到用户释放。这样的被称为 不可回收页(unreclaimable) 。 然而,在特定情况下,即便是内核数据结构占用的页面也会被回收。 例如,文件系统元数据的缓存(in-memory)可以从存储设备中重新读取,因此,当系统存在内存压力时,可以从主内存中丢弃它们。 释放可回收物理内存页并重新调整其用途的过程称为 (surprise!) reclaim 。 Linux支持异步或同步回收页,取决于系统的状态。 当系统负载不高时,大部分内存是空闲的,可以立即从空闲页得到分配。 当系统负载提升后,空闲页减少,当达到某个阈值( low watermark )时,内存分配请求将唤醒 kswapd 守护进程。它将以异步的方式扫描内存页。如果内存页中的数据在其他地方也有,则释放这些内存页;或者退出内存到后置存储设备(关联 脏页 )。 随着内存使用量进一步增加,并达到另一个阈值- min watermark -将触发回收。这种情况下,分配将暂停,直到回收到足够的内存页。 当系统运行时,任务分配并释放内存,内存变得碎片化。 虽然使用虚拟内存可以将分散的物理页表示为虚拟连续范围,但有时需要分配大的连续的物理内存。这种需求可能会提升。例如,当设备驱动需要一个大的DMA缓冲区时,或当THP分配一个大页时。 内存地址压缩(compaction ) 解决了碎片问题。 该机制将占用的页从内存zone的下部移动到上部的空闲页。压缩扫描完成后,zone开始处的空闲页就并在一起了,分配较大的连续物理内存就可行了。 与 reclaim 类似, compaction 可以在 kcompactd守护进程中异步进行,也可以作为内存分配请求的结果同步进行。 在存在负载的机器上,内存可能会耗尽,内核无法回收到足够的内存以继续运行。 为了保障系统的其余部分,引入了 OOM killer 。 OOM killer 选择牺牲一个任务来保障系统的总体健康。选定的任务被killed,以期望在它退出后释放足够的内存以继续正常的操作。
linux内核物理内存管理有哪些常用算法
Linux内核主要由五个子系统组成:进程调度,内存管理,虚拟文件系统,网络接口,进程间通信。1.进程调度(SCHED):控制进程对CPU的访问。当需要选择下一个进程运行时,由调度程序选择最值得运行的进程。可运行进程实际上是仅等待CPU资源的进程,如果某个进程在等待其它资源,则该进程是不可运行进程。Linux使用了比较简单的基于优先级的进程调度算法选择新的进程。2.内存管理(MM)允许多个进程安全的共享主内存区域。Linux 的内存管理支持虚拟内存,即在计算机中运行的程序,其代码,数据,堆栈的总量可以超过实际内存的大小,操作系统只是把当前使用的程序块保留在内存中,其余的程序块则保留在磁盘中。必要时,操作系统负责在磁盘和内存间交换程序块。内存管理从逻辑上分为硬件无关部分和硬件有关部分。硬件无关部分提供了进程的映射和逻辑内存的对换;硬件相关的部分为内存管理硬件提供了虚拟接口。3.虚拟文件系统(Virtual File System,VFS)隐藏了各种硬件的具体细节,为所有的设备提供了统一的接口,VFS提供了多达数十种不同的文件系统。虚拟文件系统可以分为逻辑文件系统和设备驱动程序。逻辑文件系统指Linux所支持的文件系统,如ext2,fat等,设备驱动程序指为每一种硬件控制器所编写的设备驱动程序模块。4.网络接口(NET)提供了对各种网络标准的存取和各种网络硬件的支持。网络接口可分为网络协议和网络驱动程序。网络协议部分负责实现每一种可能的网络传输协议。网络设备驱动程序负责与硬件设备通讯,每一种可能的硬件设备都有相应的设备驱动程序。5.进程间通讯(IPC) 支持进程间各种通信机制。
内存管理:一文读懂Linux内存组织结构及页面布局
1、内存是什么? 1) 内存又称主存,是 CPU 能直接寻址的存储空间,由半导体器件制成; 2) 内存的特点是存取速率快,断电一般不保存数据,非持久化设备; 2、内存的作用 1) 暂时存放 cpu 的运算数据 2) 硬盘等外部存储器交换的数据 3) 保障 cpu 计算机的稳定性和高性能 1、linux 内存地址空间 Linux 内存管理全貌 2、内存地址——用户态&内核态 3、内存地址——MMU 地址转换 4、内存地址——分段机制 1) 段选择符 更多Linux内核视频教程文档资料免费领取后台私信【 内核 】自行获取。 内核学习网站: Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈-学习视频教程-腾讯课堂 2) 分段实现 5、内存地址——分页机制(32 位) 6、用户态地址空间 7、内核态地址空间 8、进程内存空间 内存管理算法 ——对讨厌自己管理内存的人来说是天赐的礼物 1、内存碎片 1) 基本原理 2) 如何避免内存碎片 2、伙伴系统算法——组织结构 1) 概念 2) 外部碎片 3、伙伴系统算法——申请和回收 1) 申请算法 2) 回收算法 3) 条件 4、如何分配 4M 以上内存? 1) 为何限制大块内存分配 2) 内核中获取 4M 以上大内存的方法 5、伙伴系统——反碎片机制 1) 不可移动页 2) 可回收页 6、slab 算法——基本原理 1) 基本概念 2) 内部碎片 7、slab 分配器的结构 详细参考: 经典|图解Linux内存性能优化核心思想 8、slab 高速缓存 1) 普通高速缓存 2) 专用高速缓存 9、内核态内存池 1) 基本原理 2) 内核 API 10、用户态内存池 1) C++ 实例 11、DMA 内存 1) 什么是 DMA 2) DMA 信号 out of memory 的时代过去了吗?no,内存再充足也不可任性使用。 1、内存的使用场景 2、用户态内存分配函数 a) 如果当前连续内存块足够 realloc 的话,只是将 p 所指向的空间扩大,并返回 p 的指针地址。这个时候 q 和 p 指向的地址是一样的 b) 如果当前连续内存块不够长度,再找一个足够长的地方,分配一块新的内存,q,并将 p 指向的内容 copy 到 q,返回 q。并将 p 所指向的内存空间删除 3、内核态内存分配函数 4、malloc 申请内存 5、缺页异常 6、用户进程访问内存分析 7、共享内存 1) 原理 2) shm 接口 1、C 内存泄露 2、C 野指针 3、C 资源访问冲突 4、STL 迭代器失效 错误示例:删除当前迭代器,迭代器会失效 正确示例:迭代器 erase 时,需保存下一个迭代器 5、C++ 11 智能指针 (1)原理分析: (2)数据结构: (3)使用方法: 6、C++ 11 更小更快更安全 六、 如何查看内存 可以通过 cat /proc/slabinfo 命令查看 可以通过 /proc/sys/vm/drop_caches来释放
Linux 的虚拟内存管理有几个关键概念
Linux 的虚拟内存管理有几个关键概念:1、每个进程都有独立的虚拟地址空间,进程访问的虚拟地址并不是真正的物理地址;2、虚拟地址可通过每个进程上的页表(在每个进程的内核虚拟地址空间)与物理地址进行映射,获得真正物理地址;3、如果虚拟地址对应物理地址不在物理内存中,则产生缺页中断,真正分配物理地址,同时更新进程的页表;如果此时物理内存已耗尽,则根据内存替换算法淘汰部分页面至物理磁盘中。
linux kernel 内存管理-页表、TLB
页表用来把虚拟页映射到物理页,并且存放页的保护位(即访问权限)。 在Linux4.11版本以前,Linux内核把页表分为4级: 页全局目录表(PGD)、页上层目录(PUD)、页中间目录(PMD)、直接页表(PT) 。 4.11版本把页表扩展到5级,在页全局目录和页上层目录之间增加了 页四级目录(P4D) 。 各处处理器架构可以选择使用5级,4级,3级或者2级页表,同一种处理器在页长度不同的情况可能选择不同的页表级数。可以使用配置宏CONFIG_PGTABLE_LEVELS配置页表的级数,一般使用默认值。 如果选择4级页表,那么使用PGD,PUD,PMD,PT;如果使用3级页表,那么使用PGD,PMD,PT;如果选择2级页表,那么使用PGD和PT。 如果不使用页中间目录 ,那么内核模拟页中间目录,调用函数pmd_offset 根据页上层目录表项和虚拟地址获取页中间目录表项时 , 直接把页上层目录表项指针强制转换成页中间目录表项 。 每个进程有独立的页表,进程的mm_struct实例的成员pgd指向页全局目录,前面四级页表的表项存放下一级页表的起始地址,直接页表的页表项存放页帧号(PFN) 。 内核也有一个页表, 0号内核线程的进程描述符init_task的成员active_mm指向内存描述符init_mm,内存描述符init_mm的成员pgd指向内核的页全局目录swapper_pg_dir 。 ARM64处理器把页表称为转换表,最多4级。ARM64处理器支持三种页长度:4KB,16KB,64KB。页长度和虚拟地址的宽度决定了转换表的级数,在虚拟地址的宽度为48位的条件下,页长度和转换表级数的关系如下所示: ARM64处理器把表项称为描述符,使用64位的长描述符格式。描述符的0bit指示描述符是不是有效的:0表示无效,1表示有效。第1位指定描述符类型。 在块描述符和页描述符中,内存属性被拆分为一个高属性和一个低属性块。 处理器的MMU负责把虚拟地址转换成物理地址,为了改进虚拟地址到物理地址的转换速度,避免每次转换都需要查询内存中的页表,处理器厂商在管理单元里加了称为TLB的高速缓存,TLB直译为转换后备缓冲区,意译为页表缓存。 页表缓存用来缓存最近使用过的页表项, 有些处理器使用两级页表缓存 : 第一级TLB分为指令TLB和数据TLB,好处是取指令和取数据可以并行;第二级TLB是统一TLB,即指令和数据共用的TLB 。 不同处理器架构的TLB表项的格式不同。ARM64处理器的每条TLB表项不仅包含虚拟地址和物理地址,也包含属性:内存类型、缓存策略、访问权限、地址空间标识符(ASID)和虚拟机标识符(VMID)。 地址空间标识符区分不同进程的页表项 , 虚拟机标识符区分不同虚拟机的页表项 。 如果内核修改了可能缓存在TLB里面的页表项,那么内核必须负责使旧的TLB表项失效,内核定义了每种处理器架构必须实现的函数。 当TLB没有命中的时候,ARM64处理器的MMU自动遍历内存中的页表,把页表项复制到TLB,不需要软件把页表项写到TLB,所以ARM64架构没有提供写TLB的指令。 为了减少在进程切换时清空页表缓存的需要,ARM64处理器的页表缓存使用非全局位区分内核和进程的页表项(nG位为0表示内核的页表项), 使用地址空间标识符(ASID)区分不同进程的页表项 。 ARM64处理器的ASID长度是由具体实现定义的,可以选择8位或者16位。寄存器TTBR0_EL1或者TTBR1_EL1都可以用来存放当前进程的ASID,通常使用寄存器TCR_EL1的A1位决定使用哪个寄存器存放当前进程的ASID,通常使用寄存器 TTBR0_EL1 。寄存器TTBR0_EL1的位[63:48]或者[63:56]存放当前进程的ASID,位[47:1]存放当前进程的页全局目录的物理地址。 在SMP系统中,ARM64架构要求ASID在处理器的所有核是唯一的。假设ASID为8位,ASID只有256个值,其中0是保留值,可分配的ASID范围1~255,进程的数量可能超过255,两个进程的ASID可能相同,内核引入ASID版本号解决这个问题。 (1)每个进程有一个64位的软件ASID, 低8位存放硬件ASID,高56位存放ASID版本号 。 (2) 64位全局变量asid_generation的高56位保存全局ASID版本号 。 (3) 当进程被调度时,比较进程的ASID版本号和全局版本号 。如果版本号相同,那么直接使用上次分配的ASID,否则需要给进程重新分配硬件ASID。 存在空闲ASID,那么选择一个分配给进程。不存在空闲ASID时,把全局ASID版本号加1,重新从1开始分配硬件ASID,即硬件ASID从255回绕到1。因为刚分配的硬件ASID可能和某个进程的ASID相同,只是ASID版本号不同,页表缓存可能包含了这个进程的页表项,所以必须把所有处理器的页表缓存清空。 引入ASID版本号的好处是:避免每次进程切换都需要清空页表缓存,只需要在硬件ASID回环时把处理器的页表缓存清空 。 虚拟机里面运行的客户操作系统的虚拟地址转物理地址分两个阶段: (1) 把虚拟地址转换成中间物理地址,由客户操作系统的内核控制 ,和非虚拟化的转换过程相同。 (2) 把中间物理地址转换成物理地址,由虚拟机监控器控制 ,虚拟机监控器为每个虚拟机维护一个转换表,分配一个虚拟机标识符,寄存器 VTTBR_EL2 存放当前虚拟机的阶段2转换表的物理地址。 每个虚拟机有独立的ASID空间 ,页表缓存使用 虚拟机标识符 区分不同虚拟机的转换表项,避免每次虚拟机切换都要清空页表缓存,在虚拟机标识符回绕时把处理器的页表缓存清空。
深入理解linux虚拟内存管理 怎么样
好。很好。
linux系统编程-内存管理day05
linux实现了 请求页面调度 (在需要时将页面从硬盘交换进来,当不再需要时再交换出去),这使得系统中进程的虚拟地址空间与实际的物理内存大小没有直接的关系。 交换对进程来说是透明的,应用程序一般都不需要关心内核页面调度的行为。然而在下面 两种 情况下,应用程序可能希望影响系统的页面调度: POSIX1003.1b-1993定义两个接口将一个或多个页面“锁定”在物理内存,来保证它们不会被交换到磁盘。 调用mlock( )将锁定addr开始长度为len个字节的虚拟内存。成功时函数返回0,失败返回-1,并适当设置errno。 mlockall( )函数锁定一个进程现有的地址空间在物理内存中的所有页面。 flags参数,是下面两个值的 按位或 操作,用以控制函数行为:(大部分应用程序会同时设定这两个值) POSIX标准提供了两个接口用来将页从内存中解锁,允许内核根据需要将页换出至硬盘中。 内存锁定并不会重叠,所以不管mlock( )或mlockall( )了几次,仅一个munlock( )或munlockall( )会解除一个页面的锁定。 linux对于一个进程能锁定的页面数进行了限制:拥有 CAP_IPC_LOCK 权限的进程能锁定 任意多 的页面。没有这个权限的进程只能锁定 RLIMIT_MEMLOCK 个字节,默认情况下,该限制是 32KB 。 mincore( )函数,用来确定一个给定范围的内存是在物理内存中还是被交换到了硬盘中: 函数通过vec来返回向量,这个向量描述start(必须页面对齐)开始长为length(不需要对齐)字节的内存中的页面的情况。 Linux使用 投机性分配策略 :当一个进程向内核请求额外的内存-如扩大它的数据段,或者创建一个新的存储器映射-内核作出了分配承诺但 实际上并没有分给进程任何的物理存储 。 这样处理有如下几个 优点 : 超量使用的好处:和在应用请求页面就分配物理存储相比, 在使用时刻才分配物理存储的过量使用机制允许系统运行更多,更大的应用程序 。 但是,如果系统中的进程为满足超量使用而申请的内存大于物理内存和交换空间之和,内核只能杀死另一个进程并释放它的内存,以此来满足下一次的分配需求。 内核允许通过文件/proc/sys/vm/overcommit_memory关闭超量使用,和此功能相似的还有sysctl的vm.overcommit_memory参数。 在严格审计模式中,承诺的内存大小被严格限制在交换空间的大小加上 可调比例 的物理内存大小。 使用严格审计策略时要非常小心!许多系统设计者认为严格审计策略才是解决之道,然而, 应用程序常常进行一些不必要的、且只有使用超量使用才能满足的分配请求,而允许这种行为也是设计虚拟内存的主要动机之一。
Linux进程内存管理?
对于包含MMU的处理器而言,Linux系统提供了复杂的存储管理系统,使得进程所能访问的内存达到4GB。在Linux系统中,进程的4GB内存空间被分为两个部分——用户空间与内核空间。用户空间的地址一般分布为0~3GB(即PAGE_OFFSET,在Ox86中它等于OxC0000000),这样,剩下的3~4GB为内核空间,用户进程通常只能访问用户空间的虚拟地址,不能访问内核空间的虚拟地址。用户进程只有通过系统调用(代表用户进程在内核态执行)等方式才可以访问到内核空间。每个进程的用户空间都是完全独立、互不相干的,用户进程各自有不同的页表。而内核空间是由内核负责映射,它并不会跟着进程改变,是固定的。内核空间的虚拟地址到物理地址映射是被所有进程共享的,内核的虚拟空间独立于其他程序。Linux中1GB的内核地址空间又被划分为物理内存映射区、虚拟内存分配区、高端页面映射区、专用页面映射区和系统保留映射区这几个区域。对于x86系统而言,一般情况下,物理内存映射区最大长度为896MB,系统的物理内存被顺序映射在内核空间的这个区域中。当系统物理内存大于896MB时,超过物理内存映射区的那部分内存称为高端内存(而未超过物理内存映射区的内存通常被称为常规内存),内核在存取高端内存时必须将它们映射到高端页面映射区。Linux保留内核空间最顶部FIXADDR_TOP~4GB的区域作为保留区。当系统物理内存超过4GB时,必须使用CPU的扩展分页(PAE)模式所提供的64位页目录项才能存取到4GB以上的物理内存,这需要CPU的支持。加入了PAE功能的Intel Pentium Pro及以后的CPU允许内存最大可配置到64GB,它们具备36位物理地址空间寻址能力。由此可见,对于32位的x86而言,在3~4GB之间的内核空间中,从低地址到高地址依次为:物理内存映射区→隔离带→vmalloc虚拟内存分配器区→隔离带→高端内存映射区→专用页面映射区→保留区。
数据库内存不足,如何设置内存管理?
1、主要设置minservermemory和maxservermemory,如果同台服务器有多台吃内存大户服务,maxservermemory给设置一个值稍大的值出来,别让SQLserver把WINDOWS内存都吃完了也别让其他的服务把WINDOWS内存吃完了,这样才会相安无事2.设置方法可以通过命令方式的,楼上的挺好,还有就是通过SQLSERVER企业管理器进行:鼠标右键服务器属性内存可以看多最大内存和最小内存设置选项。
什么是内存管理
管理手机里储存空间,,
简述内存管理中buddy算法和slab机制的区别
1、Buddy算法linux对空闲内存空间管理采取buddy算法,Buddy算法:把内存中所有页面按照2^n划分,其中n=0~5,每个内存空间按1个页面、2个页面、4个页面、8个页面、16个页面、32个页面进行六次划分。划分后形成了大小不等的存储块,称为页面块,简称页块,包含一个页面的页块称为1页块,包含2个页面的称为2页块,依次类推。每种页块按前后顺序两两结合成一对Buddy“伙伴”。系统按照Buddy关系把具有相同大小的空闲页面块组成页块组,即1页块组、2页块组……32页块组。 每个页块组用一个双向循环链表进行管理,共有6个链表,分别为1、2、4、8、16、32页块链表。分别挂到free_area[] 数组上。位图数组用于标记内存页面使用情况,第0组每一位表示单个页面使用情况,1表示使用,0表示空闲,第二组每一位表示比邻的两个页面使用情况,一次类推。默认为10个数组,当一对Buddy的两个页面中有一个事空闲的,而另一个全部或部分被占用时,该位置1.两个页面块都是空闲,对应位置0.内存分配和释放过程内存分配时,系统按照Buddy算法,根据请求的页面数在free_area[]对应的空闲页块组中搜索。 若请求页面数不是2的整数次幂,则按照稍大于请求数的2的整数次幂的值搜索相应的页面块组。当相应页块组中没有可使用的空闲页面块时就查询更大一些的页块组,在找到可用的页块后分配所需要的页面。当某一空闲页面被分配后,若仍有剩余的空闲页面,则根据剩余页面的大小把他们加入到相应页面组中。内存页面释放时,系统将其作为空闲页面看待,检查是否存在与这些页面相邻的其他空闲页块,若存在,则合为一个连续的空闲区按Buddy算法重新分组。2、Slab算法采用buddy算法,解决了外碎片问题,这种方法适合大块内存请求,不适合小内存区请求。如:几十个或者几百个字节。Linux2.0采用传统内存分区算法,按几何分布提供内存区大小,内存区以2的幂次方为单位。虽然减少了内碎片,但没有显著提高系统效率。Linux2.4采用了slab分配器算法,该算法比传统的分配器算法有更好性能和内存利用率,最早在solaris2.4上使用。Slab分配器思想1)小对象的申请和释放通过slab分配器来管理。2)slab分配器有一组高速缓存,每个高速缓存保存同一种对象类型,如i节点缓存、PCB缓存等。3)内核从它们各自的缓存种分配和释放对象。4)每种对象的缓存区由一连串slab构成,每个slab由一个或者多个连续的物理页面组成。这些页面种包含了已分配的缓存对象,也包含了空闲对象。
关于Linux-0.11内核_段页内存管理的问题
目前还没有想法!
怎么卸载内存管理器
软件拒绝卸载一般是有“设备管理器”权限或者是病毒程序,请按照以下步骤进行解决:备份重要的资料和信息(例如:通讯录,短信,照片等)以防在杀毒过程中丢失或损坏。安装杀毒软件(例如:手机管家)进行一键查杀,以手机管家为例,打开手机管家,点击“安全防护”,点击“立即扫描”即可一键杀毒。对于绝大多数病毒,请获取ROOT权限后可以彻底清除,防止死灰复燃,若依然无法处理,请到手机官方售后进行系统修复。到系统设置,安全,设备管理中关闭相关权限即可正常卸载。
常用的内存管理方法有哪些?
下一个 z武器 或者优化大师就非常清楚了
内存管理的内存对象
Windows应用程序可以申请分配属于自己的内存块,内存块是应用程序操作内存的单位,它也称作内存对象,在Windows中通过内存句柄来操作内存对象。内存对象根据分配的范围可分为全局内存对象和局部内存对象;根据性质可分为固定内存对象,可移动内存对象和可删除内存对象。固定内存对象,特别是局部固定内存对象和DOS的内存块很类似,它一旦分配,就不会被移动或删除,除非应用程序主动释放它。并且对于局部固定内存对象来说,它的内存句柄本身就是内存对象的16位近地址,可供应用程序直接存取,而不必象其它类型的内存对象那样要通过锁定在内存某固定地址后才能使用。可移动内存对象没有固定的地址,Windows系统可以随时把它们移到一个新地址。内存对象的可移动使得Windows能有效地利用自由内存。例如,如果一个可移动的内存对象分开了两个自由内存对象,Windows可以把可移动内存对象移走,将两个自由内存对象合并为一个大的自由内存对象,实现内存的合并与碎片回收。可删除内存对象与可移动内存对象很相似,它可以被Windows移动,并且当Windows需要大的内存空间满足新的任务时,它可以将可删除内存对象的长度置为0,丢弃内存对象中的数据。可移动内存对象和可删除内存对象在存取前必须使用内存加锁函数将其锁定,锁定了的内存对象不能被移动和删除。因此,应用程序在使用完内存对象后要尽可能快地为内存对象解锁。内存需要加锁和解锁增加了程序员的负担,但是它却极大地改善了Windows内存利用的效率,因此Windows鼓励使用可移动和可删除的内存对象,并且要求应用程序在非必要时不要使用固定内存对象。不同类型的对象在它所处的内存堆中的位置是不一样的,图6.2说明内存对象在堆中的位置:固定对象位于堆的底部;可移动对象位于固定对象之上;可删除对象从堆的顶部开始分配。 局部内存对象在局部堆中分配,局部堆是应用程序独享的自由内存,它只能由应用程序的特定实例访问。局部堆建立在应用程序的数据段中,因此,用户可分配的局部内存对象的最大内存空间不能超过64K。局部堆由Windows应用程序在模块定义文件中用HEAPSIZE语句申请,HEAPSIZE指定以字节为单位的局部堆初始空间尺寸。Windows提供了一系列函数来操作局部内存对象。分配局部内存对象LocalAlloc函数用来分配局部内存,它在应用程序局部堆中分配一个内存块,并返回内存块的句柄。LocalAlloc函数可以指定内存对象的大小和特性,其中主要特性有固定的(LMEM_FIXED),可移动的(LMEM_MOVEABLE)和可删除的(LMEM_DISCARDABLE)。如果局部堆中无法分配申请的内存,则LocalAlloc函数返回NULL。下面的代码用来分配一个固定内存对象,因为局部固定内存对象的对象句柄其本身就是16位内存近地址,因此它可以被应用程序直接存取。加锁与解锁上面程序段分配的固定局部内存对象可以由应用程序直接存取,但是,Windows并不鼓励使用固定内存对象。因此,在使用可移动和可删除内存对象时,就要经常用到对内存对象的加锁与解锁。不管是可移动对象还是可删除对象,在它分配后其内存句柄是不变的,它是内存对象的恒定引用。但是,应用程序无法通过内存句柄直接存取内存对象,应用程序要存取内存对象还必须获得它的近地址,这通过调用LocalLock函数实现。LocalLock函数将局部内存对象暂时固定在局部堆的某一位置,并返回该地址的近地址值,此地址可供应用程序存取内存对象使用,它在应用程序调用 LocalUnlock函数解锁此内存对象之前有效。应用程序在使用完内存对象后,要尽可能早地为它解锁,这是因为Windows无法移动被锁住了的内存对象。当应用程序要分配其它内存时,Windows不能利用被锁住对象的区域,只能在它周围寻找,这会降低Windows内存管理的效率。改变局部内存对象局部内存对象分配之后,还可以调用LocalReAlloc函数进行修改。LocalReAlloc函数可以改变局部内存对象的大小而不破坏其内容:如果比原来的空间小,则Windows将对象截断;如果比原来大,则Windows将增加区域填0(使用LMEM_ZEROINIT选项),或者不定义该区域内容。另外,LocalReAlloc函数还可以改变对象的属性,如将属性从LMEM_MOVEABLE改为LMEM_DISCARDABLE,或反过来,此时必须同时指定LMEM_MODIFY选项。但是,LocalReAlloc函数不能同时改变内存对象的大小和属性,也不能改变具有LMEM_FIXED属性的内存对象和把其它属性的内存对象改为LMEM_FIXED属性。释放与删除分配了的局部内存对象可以使用LocalDiscard和LocalFree函数来删除和释放,删除和释放只有在内存对象未锁住时才有效。LocalFree函数用来释放局部内存对象,当一个局部内存对象被释放时,其内容从局部堆移走,并且其句柄也从有效的局部内存表中移走,原来的内存句柄变为不可用。LocalDiscard 函数用来删除局部内存对象,它只移走对象的内容,而保持其句柄有效,用户在需要时,还可以使用此内存句柄用LocalReAlloc函数重新分配一块内存。另外,Windows还提供了函数LocalSize用于检测对象所占空间;函数LocalFlags用于检测内存对象是否可删除,是否已删除,及其锁计数值;函数LocalCompact用于确定局部堆的可用内存。 全局内存对象在全局堆中分配,全局堆包括所有的系统内存。一般来说,应用程序在全局堆中进行大型内存分配(约大于1KB),在全局堆还可以分配大于64K的巨型内存,这将在后面介绍。分配全局内存对象全局内存对象使用GlobalAlloc函数分配,它和使用LocalAlloc分配局部内存对象很相似。使用GlobalAlloc的例子我们将和GlobalLock一起给出。加锁与解锁全局内存对象使用GlobalLock函数加锁,所有全局内存对象在存取前都必须加锁。GlobalLock将对象锁定在内存固定位置,并返回一个远指针,此指针在调用GlobalUnlock之前保持有效。GlobalLock和LocalLock稍有不同,因为全局内存对象可能被多个任务使用,因此在使用GlobalLock加锁某全局内存对象时,对象可能已被锁住,为了处理这种情况,Windows增加了一个锁计数器。当使用GlobalLock加锁全局内存对象时,锁计数器加1;使用GlobalUnlock解锁对象时,锁计数器减1,只有当锁计数器为0时,Windows才真正解锁此对象。修改全局内存对象修改全局内存对象使用GlobalReAlloc函数,它和LocalReAlloc函数很类似,这里不再赘述。修改全局内存对象的特殊之处在于巨型对象的修改上,这一点我们将在后面讲述。内存释放及其它操作全局内存对象使用GlobalFree函数和GlobalDiscard来释放与删除,其作用与LocalFree和LocalDiscard类似。GlobalSize函数可以检测内存对象大小;GlobalFlags函数用来检索对象是否可删除,是否已删除等信息;GlobalCompact函数可以检测全局堆可用内存大小。巨型内存对象如果全局内存对象的大小为64KB或更大,那它就是一个巨型内存对象,使用GlobalLock函数加锁巨型内存对象将返回一个巨型指针。巨型内存对象的修改有一点特殊性,当对象大小增加并超过64K的倍数时,Windows可能要为重新分配的内存对象返回一个新的全局句柄, Windows采用段的概念来管理应用程序的内存,段有代码段和数据段两种,一个应用程序可有多个代码段和数据段。代码段和数据段的数量决定了应用程序的内存模式,图6.2说明了内存模式与应用程序代码段和数据段的关系。段的管理和全局内存对象的管理很类似,段可以是固定的,可移动的和可删除的,其属性在应用程序的模块定义文件中指定。段在全局内存中分配空间,Windows鼓励使用可移动的代码段和数据段,这样可以提高其内存利用效率。使用可删除的代码段可以进一步减小应用程序对内存的影响,如果代码段是可删除的,在必要时Windows将其删除以满足对全局内存的请求。被删除的段由Windows监控,当应用程序利用该代码段时,Windows自动地将它们重新装入。代码段代码段是不超过64K字节的机器指令,它代表全部或部分应用程序指令。代码段中的数据是只读的,对代码段执行写操作将引起通用保护(GP)错误。每个应用程序都至少有一个代码段,例如我们前面几章的例子都只有一个代码段。用户也可以生成有多个代码段的应用。实际上,多数Windows应用程序都有多个代码段。通过使用多代码段,用户可以把任何给定代码段的大小减少到完成某些任务所必须的几条指令。这样,可通过使某些段可删除,来优化应用程序对内存的使用。中模式和大模式的应用程序都使用多代码段,这些应用程序的每一个段都有一个或几个源文件。对于多个源文件,将它们分开各自编译,为编译过的代码所属的每个段命名,然后连接。段的属性在模块定义文件中定义,Windows使用SEGMENTS语句来完成此任务,如下面的代码定义了四个段的属性:用户也可以在模块定义文件中用CODE语句为所有未显式定义过的代码段定义缺省属性。例如,要将未列在SEGMENTS语句中的所有段定义为可删除的,可用下面的语句:CODE MOVEABLE DISCARDABLE。数据段每个应用程序都有一个数据段,数据段包含应用程序的堆栈、局部堆、静态数据和全局数据。一个数据段的长度也不能超过64K。数据段可以是固定的或可移动的,但不能是可删除的。如果数据段是可移动的,Windows在将控制转向应用程序前自动为其加锁,当应用程序分配全局内存,或试图在局部堆中分配超过当前可分的内存时,可移动数据段可能被移动,因此在数据段中不要保留指向变量的长指针,当数据段移动时,此长指针将失效。在模块定义文件中用DATA语句定义数据段的属性,属性的缺省值为MOVEABLE和MULTIPLE。MULTIPLE属性使Windows为应用程序的每一个实例拷贝一个应用程序数据段,这就是说每个应用程序实例中数据段的内容都是不同的。内存管理程序示例Memory应用程序Memory示例了部分内存管理,它是一个使用了可删除代码段的中模式Windows应用程序。Memory程序有四个C语言源程序,在模块定义文件中显示定义了四个代码段,相应地模块定义文件和makefile文件有地些修改,读者可通过比较Memory程序和5.1.2节的例子来体会它们之间的不同。另外,读者在编译和连接应用程序Memory后,可用Visual C++提供的Windows Heap Walker (HEAPWALK.EXE)来观察Memory运行时的各个段。 使用动态连接库是Windows的一个很重要的特点,它使得多个Windows应用程序可以共享函数代码、数据和硬件,这可以大大提高Windows内存的利用率。动态连接库是一个可执行模块,它包含的函数可以由Windows应用程序调用执行,为应用程序提供服务。它和我们以前用的C函数库相比,在功能上是很类似的,其主要区别是动态连接库在运行是连接,C函数库(静态连接库)是在生成可执行文件时由连接器(LINK)连接。静态连接库中的代码在应用程序生成以后已经连接到应用程序模块之中,但动态连接库中的代码只有在应用程序要用到该代码段时才动态调入DLL中的相应代码。为了让应用程序在执行时能够调入DLL中正确的代码,Windows提供了动态连接库的引入库。Windows在连接生成应用程序时,如果使用动态连接库函数,连接器并不拷贝DLL中的任何代码,它只是将引入库中指定所需函数在DLL中位置的信息拷贝在应用程序模块中,当应用程序运行时,这些定位信息在可执行应用程序和动态连接库之间建立动态连接。静态库、引入库和动态库之间的区别如表6.1所示。DLL不能独立执行,也不能使用消息循环。每个DLL都有一个入口点和一个出口点,具有自己的实例句柄、数据段和局部堆,但DLL没有堆栈,它使用调用程序的堆栈。DLL也包括有.C文件,.H文件,.RC文件和.DEF文件,另外,在连接时一般要加入SDK库中的LIBENTRY.OBJ文件。创建动态连接库要创建动态连接库,至少有三个文件:C语言源文件;一个模块定义文件(.DEF);makefile文件。有了这些文件后,就可以运行Microsoft的程序维护机制(NMAKE),编译并连接源代码文件,生成DLL文件。创建C语言源文件和其它C应用程序一样,动态连接库可包含多个函数,每个函数要在被其它应用程序或库使用之前用FAR声明,并且在库的模块定义文件中用EXPORTS语句引出。在上面的源代码中,有两个函数是DLL源代码所必需的,这就是DLL入口函数LibMain和出口函数WEP。LibMain函数是DLL的入口点,它由DLL 自动初始化函数LibEntry调用,主要用来完成一些初始化任务。LibMain有四个参数:hint, wDataSeg, cbHeapSize和lpszCmdLine。其中hInst是动态连接库的实例句柄;wDataSeg是数据段(DS)寄存器的值;cbHeapSize是模块定义文件定义的堆的尺寸,LibEntry函数用该值来初始化局部堆;lpszCmdLine包含命令行的信息。WEP函数是DLL的标准出口函数,它在DLL被卸出之前由Windows调用执行,以完成一些必要的清除工作。WEP函数只使用一个参数nParameter,它用来指示终止状态。源文件中的其它函数则是DLL为应用程序提供的库函数,DLL设计者可以给它加入自己所需要的功能,如DrawBox,DrawPie和DrawCircle。建立DLL模块定义文件每个DLL必须有一个模块定义文件,该文件在使用LINK连接时用于提供定义库属性的引入信息。关键字LIBRARY用来标识这个模块是一个动态连接库,其后是库名DRAWDLL,它必须和动态连接库文件名相同。DATA语句中关键字SINGLE是必须的,它表明无论应用程序访问DLL多少次,DLL均只有单个数据段。其它关键字的用法同Windows应用程序的模块定义文件一样,这在前面已有叙述,请参见5.1.2.3。编制Makefile文件NMAKE是Microsoft的程序维护机制,它控制执行文件的创建工作,以保证只有必要的操作被执行。有五种工具用来创建动态连接库:CLMicrosoft C优化编译器,它将C语言源文件编译成目标文件.OBJ。LINKMicrosoft 分段可执行连接器,它将目标文件和静态库连接生成动态连接库。LINK命令行有五个参数,用逗号分开:第一个参数列出所有动态连接库用到的目标文件(.OBJ),如果使用了标准动态连接初始化函数,则必须包括LIBENTRY.OBJ文件;第二个参数指示最终可执行文件名,一般用.DLL作为扩展名;第三个参数列出创建动态连接库所需要的引入库和静态库;第五个参数是模块定义文件。IMPLIBMicrosoft引入库管理器,它根据动态连接库的模块定义文件创建一个扩展名为.LIB的引入库。RCMicrosoft Windows资源编译器。所有动态连接库都必须用RC编译,以使它们与Windows 3.1版兼容。MAPSYMMicrosoft符号文件生成器,它是可选工具,只用于调试版本。 应用程序要访问动态连接库函数,它应该做下面三件事:建立库函数原型,调用库函数,引入库函数。建立库函数原型一般通过在C语言源文件中包含动态连接库的头文件解决,头文件中包含了每个库函数的原型语句,原型语句的目的是为编译器定义函数的参数和返回值,以使编译器能正确创建调用库函数的代码。原型语句定义好之后,应用程序就可以象调用静态连接库函数一样调用动态连接库的函数了。应用程序调用DLL中的引出函数还要在应用程序中对其进行引入,一般有三种方法:连接时隐式引入最常用也最简单的方法是连接时隐式引入,这种方法是在应用程序的连接命令行中列出为动态连接库创建的引入库,这样应用程序在使用DLL的引出函数时,就如同使用静态库中的函数一样了。连接时显式引入和隐式引入一样,显式引入也是在连接时进行的,它通过把所需函数列在应用程序的模块定义文件的IMPORTS语句中完成。对于在模块定义文件中定义了入口序号的DLL函数,采用引入函数名、动态连接库名和入口序号的形式,如:IMPORTSDrawBox=DllDraw.2如果DLL的模块定义文件没有定义引出函数的入口序号,则使用如下引入语句:IMPORTSDllDraw.DrawBox运行时动态引入 应用程序可以在运行时动态连接DLL函数,当需要调用DLL的引出函数时,应用程序首先装入库,并直接检索所需函数地址,然后才调用该函数。
常用的内存管理方法有哪几种
常用的内存管理 方法 有哪几种?下面是我给大家收集整理的一些相关方法技巧,希望对大家有帮助! 常用的内存管理方法 传统的内存整理软件工作原理大概是:先申请一块“巨大内存”。因为物理内存几乎全被内存整理软件占用,因此Windows被迫把其他软件的内存数据转移到硬盘上的“虚拟内存交换文件”(PageFile)中,完成这一过程之后内存整理软件就会释放掉刚刚申请的内存,至此整理过程完成,可用物理内存显著增加。 大体上都是那么回事,就是通过辅助空间,重新安排内存内容 .... 但是其中使用的算法,效率是有很大的区别的 ~~ <script type="text/javascript"><!-- google_ad_client = "pub-4403405132739389"; google_ad_width = 250; google_ad_height = 250; google_ad_format = "250x250_as"; google_ad_type = "text"; //2007-10-22: 250*250 google_ad_channel = "7687946060"; google_ui_features = "rc:10"; //--> </script><script type="text/javascript" src=pagead2.googlesyndication/pagead/show_ads.js"> </script> 拓荒时代 国内的程序员大多是在 Java 语言中第一次感受到垃圾收集技术的巨大魅力的,许多人也因此把 Java 和垃圾收集看成了密不可分的整体。但事实上,垃圾收集技术早在 Java 语言问世前 30 多年就已经发展和成熟起来了, Java 语言所做的不过是把这项神奇的技术带到了广大程序员身边而已。 如果一定要为垃圾收集技术找一个孪生兄弟,那么, Lisp 语言才是当之无愧的人选。 1960 年前后诞生于 MIT 的 Lisp 语言是第一种高度依赖于动态内存分配技术的语言: Lisp 中几乎所有数据都以“表”的形式出现,而“表”所占用的空间则是在堆中动态分配得到的。 Lisp 语言先天就具有的动态内存管理特性要求 Lisp 语言的设计者必须解决堆中每一个内存块的自动释放问题(否则, Lisp 程序员就必然被程序中不计其数的 free 或 delete 语句淹没),这直接导致了垃圾收集技术的诞生和发展——说句题外话,上大学时,一位老师曾告诉我们, Lisp 是对现代软件开发技术贡献最大的语言。我当时对这一说法不以为然:布满了圆括号,看上去像迷宫一样的 Lisp 语言怎么能比 C 语言或 Pascal 语言更伟大呢?不过现在,当我知道垃圾收集技术、数据结构技术、人工智能技术、并行处理技术、虚拟机技术、元数据技术以及程序员们耳熟能详的许多技术都起源于 Lisp 语言时,我特别想向那位老师当面道歉,并收回我当时的幼稚想法。 知道了 Lisp 语言与垃圾收集的密切关系,我们就不难理解,为什么垃圾收集技术的两位先驱者 J. McCarthy 和 M. L. Minsky 同时也是 Lisp 语言发展史上的重要人物了。 J. McCarthy 是 Lisp 之父,他在发明 Lisp 语言的同时也第一次完整地描述了垃圾收集的算法和实现方式; M. L. Minsky 则在发展 Lisp 语言的过程中成为了今天好几种主流垃圾收集算法的奠基人——和当时不少技术大师的经历相似, J. McCarthy 和 M. L. Minsky 在许多不同的技术领域里都取得了令人艳羡的成就。也许,在 1960 年代那个软件开发史上的拓荒时代里,思维敏捷、意志坚定的研究者更容易成为无所不能的西部硬汉吧。 在了解垃圾收集算法的起源之前,有必要先回顾一下内存分配的主要方式。我们知道,大多数主流的语言或运行环境都支持三种最基本的内存分配方式,它们分别是: 一、静态分配( Static Allocation ):静态变量和全局变量的分配形式。我们可以把静态分配的内存看成是家里的耐用家具。通常,它们无需释放和回收,因为没人会天天把大衣柜当作垃圾扔到窗外。 二、自动分配( Automatic Allocation ):在栈中为局部变量分配内存的方法。栈中的内存可以随着代码块退出时的出栈操作被自动释放。这类似于到家中串门的访客,天色一晚就要各回各家,除了个别不识时务者以外,我们一般没必要把客人捆在垃圾袋里扫地出门。 三、动态分配( Dynamic Allocation ):在堆中动态分配内存空间以存储数据的方式。堆中的内存块好像我们日常使用的餐巾纸,用过了就得扔到垃圾箱里,否则屋内就会满地狼藉。像我这样的懒人做梦都想有一台家用机器人跟在身边打扫卫生。在软件开发中,如果你懒得释放内存,那么你也需要一台类似的机器人——这其实就是一个由特定算法实现的垃圾收集器。 也就是说,下面提到的所有垃圾收集算法都是在程序运行过程中收集并清理废旧“餐巾纸”的算法,它们的操作对象既不是静态变量,也不是局部变量,而是堆中所有已分配内存块。 引用计数( Reference Counting )算法 1960 年以前,人们为胚胎中的 Lisp 语言设计垃圾收集机制时,第一个想到的算法是引用计数算法。拿餐巾纸的例子来说,这种算法的原理大致可以描述为: 午餐时,为了把脑子里突然跳出来的设计灵感记下来,我从餐巾纸袋中抽出一张餐巾纸,打算在上面画出系统架构的蓝图。按照“餐巾纸使用规约之引用计数版”的要求,画图之前,我必须先在餐巾纸的一角写上计数值 1 ,以表示我在使用这张餐巾纸。这时,如果你也想看看我画的蓝图,那你就要把餐巾纸上的计数值加 1 ,将它改为 2 ,这表明目前有 2 个人在同时使用这张餐巾纸(当然,我是不会允许你用这张餐巾纸来擦鼻涕的)。你看完后,必须把计数值减 1 ,表明你对该餐巾纸的使用已经结束。同样,当我将餐巾纸上的内容全部誊写到 笔记本 上之后,我也会自觉地把餐巾纸上的计数值减 1 。此时,不出意外的话,这张餐巾纸上的计数值应当是 0 ,它会被垃圾收集器——假设那是一个专门负责打扫卫生的机器人——捡起来扔到垃圾箱里,因为垃圾收集器的惟一使命就是找到所有计数值为 0 的餐巾纸并清理它们。 引用计数算法的优点和缺陷同样明显。这一算法在执行垃圾收集任务时速度较快,但算法对程序中每一次内存分配和指针操作提出了额外的要求(增加或减少内存块的引用计数)。更重要的是,引用计数算法无法正确释放循环引用的内存块,对此, D. Hillis 有一段风趣而精辟的论述: 一天,一个学生走到 Moon 面前说:“我知道如何设计一个更好的垃圾收集器了。我们必须记录指向每个结点的指针数目。” Moon 耐心地给这位学生讲了下面这个 故事 :“一天,一个学生走到 Moon 面前说:u2018我知道如何设计一个更好的垃圾收集器了u2026u2026u2019” D. Hillis 的故事和我们小时候常说的“从前有座山,山上有个庙,庙里有个老和尚”的故事有异曲同工之妙。这说明,单是使用引用计数算法还不足以解决垃圾收集中的所有问题。正因为如此,引用计数算法也常常被研究者们排除在狭义的垃圾收集算法之外。当然,作为一种最简单、最直观的解决方案,引用计数算法本身具有其不可替代的优越性。 1980 年代前后, D. P. Friedman , D. S. Wise , H. G. Baker 等人对引用计数算法进行了数次改进,这些改进使得引用计数算法及其变种(如延迟计数算法等)在简单的环境下,或是在一些综合了多种算法的现代垃圾收集系统中仍然可以一展身手。 标记-清除( Mark-Sweep )算法 第一种实用和完善的垃圾收集算法是 J. McCarthy 等人在 1960 年提出并成功地应用于 Lisp 语言的标记-清除算法。仍以餐巾纸为例,标记-清除算法的执行过程是这样的: 午餐过程中,餐厅里的所有人都根据自己的需要取用餐巾纸。当垃圾收集机器人想收集废旧餐巾纸的时候,它会让所有用餐的人先停下来,然后,依次询问餐厅里的每一个人:“你正在用餐巾纸吗?你用的是哪一张餐巾纸?”机器人根据每个人的回答将人们正在使用的餐巾纸画上记号。询问过程结束后,机器人在餐厅里寻找所有散落在餐桌上且没有记号的餐巾纸(这些显然都是用过的废旧餐巾纸),把它们统统扔到垃圾箱里。 正如其名称所暗示的那样,标记-清除算法的执行过程分为“标记”和“清除”两大阶段。这种分步执行的思路奠定了现代垃圾收集算法的思想基础。与引用计数算法不同的是,标记-清除算法不需要运行环境监测每一次内存分配和指针操作,而只要在“标记”阶段中跟踪每一个指针变量的指向——用类似思路实现的垃圾收集器也常被后人统称为跟踪收集器( Tracing Collector ) 伴随着 Lisp 语言的成功,标记-清除算法也在大多数早期的 Lisp 运行环境中大放异彩。尽管最初版本的标记-清除算法在今天看来还存在效率不高(标记和清除是两个相当耗时的过程)等诸多缺陷,但在后面的讨论中,我们可以看到,几乎所有现代垃圾收集算法都是标记-清除思想的延续,仅此一点, J. McCarthy 等人在垃圾收集技术方面的贡献就丝毫不亚于他们在 Lisp 语言上的成就了。 复制( Copying )算法 为了解决标记-清除算法在垃圾收集效率方面的缺陷, M. L. Minsky 于 1963 年发表了著名的论文“一种使用双存储区的 Lisp 语言垃圾收集器( A LISP Garbage Collector Algorithm Using Serial Secondary Storage )”。 M. L. Minsky 在该论文中描述的算法被人们称为复制算法,它也被 M. L. Minsky 本人成功地引入到了 Lisp 语言的一个实现版本中。 复制算法别出心裁地将堆空间一分为二,并使用简单的复制操作来完成垃圾收集工作,这个思路相当有趣。借用餐巾纸的比喻,我们可以这样理解 M. L. Minsky 的复制算法: 餐厅被垃圾收集机器人分成南区和北区两个大小完全相同的部分。午餐时,所有人都先在南区用餐(因为空间有限,用餐人数自然也将减少一半),用餐时可以随意使用餐巾纸。当垃圾收集机器人认为有必要回收废旧餐巾纸时,它会要求所有用餐者以最快的速度从南区转移到北区,同时随身携带自己正在使用的餐巾纸。等所有人都转移到北区之后,垃圾收集机器人只要简单地把南区中所有散落的餐巾纸扔进垃圾箱就算完成任务了。下一次垃圾收集的工作过程也大致类似,惟一的不同只是人们的转移方向变成了从北区到南区。如此循环往复,每次垃圾收集都只需简单地转移(也就是复制)一次,垃圾收集速度无与伦比——当然,对于用餐者往返奔波于南北两区之间的辛劳,垃圾收集机器人是决不会流露出丝毫怜悯的。 M. L. Minsky 的发明绝对算得上一种奇思妙想。分区、复制的思路不仅大幅提高了垃圾收集的效率,而且也将原本繁纷复杂的内存分配算法变得前所未有地简明和扼要(既然每次内存回收都是对整个半区的回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存就可以了),这简直是个奇迹!不过,任何奇迹的出现都有一定的代价,在垃圾收集技术中,复制算法提高效率的代价是人为地将可用内存缩小了一半。实话实说,这个代价未免也太高了一些。 无论优缺点如何,复制算法在实践中都获得了可以与标记-清除算法相比拟的成功。除了 M. L. Minsky 本人在 Lisp 语言中的工作以外,从 1960 年代末到 1970 年代初, R. R. Fenichel 和 J. C. Yochelson 等人也相继在 Lisp 语言的不同实现中对复制算法进行了改进, S. Arnborg 更是成功地将复制算法应用到了 Simula 语言中。 至此,垃圾收集技术的三大传统算法——引用计数算法、标记-清除算法和复制算法——都已在 1960 年前后相继问世,三种算法各有所长,也都存在致命的缺陷。从 1960 年代后期开始,研究者的主要精力逐渐转向对这三种传统算法进行改进或整合,以扬长避短,适应程序设计语言和运行环境对垃圾收集的效率和实时性所提出的更高要求。 走向成熟 从 1970 年代开始,随着科学研究和应用实践的不断深入,人们逐渐意识到,一个理想的垃圾收集器不应在运行时导致应用程序的暂停,不应额外占用大量的内存空间和 CPU 资源,而三种传统的垃圾收集算法都无法满足这些要求。人们必须提出更新的算法或思路,以解决实践中碰到的诸多难题。当时,研究者的努力目标包括: 第一,提高垃圾收集的效率。使用标记-清除算法的垃圾收集器在工作时要消耗相当多的 CPU 资源。早期的 Lisp 运行环境收集内存垃圾的时间竟占到了系统总运行时间的 40% !——垃圾收集效率的低下直接造就了 Lisp 语言在执行速度方面的坏名声;直到今天,许多人还条件反射似地误以为所有 Lisp 程序都奇慢无比。 第二,减少垃圾收集时的内存占用。这一问题主要出现在复制算法中。尽管复制算法在效率上获得了质的突破,但牺牲一半内存空间的代价仍然是巨大的。在计算机发展的早期,在内存价格以 KB 计算的日子里,浪费客户的一半内存空间简直就是在变相敲诈或拦路打劫。 第三,寻找实时的垃圾收集算法。无论执行效率如何,三种传统的垃圾收集算法在执行垃圾收集任务时都必须打断程序的当前工作。这种因垃圾收集而造成的延时是许多程序,特别是执行关键任务的程序没有办法容忍的。如何对传统算法进行改进,以便实现一种在后台悄悄执行,不影响——或至少看上去不影响——当前进程的实时垃圾收集器,这显然是一件更具挑战性的工作。 研究者们探寻未知领域的决心和研究工作的进展速度同样令人惊奇:在 1970 年代到 1980 年代的短短十几年中,一大批在实用系统中表现优异的新算法和新思路脱颖而出。正是因为有了这些日趋成熟的垃圾收集算法,今天的我们才能在 Java 或 .NET 提供的运行环境中随心所欲地分配内存块,而不必担心空间释放时的风险。 标记-整理( Mark-Compact )算法 标记-整理算法是标记-清除算法和复制算法的有机结合。把标记-清除算法在内存占用上的优点和复制算法在执行效率上的特长综合起来,这是所有人都希望看到的结果。不过,两种垃圾收集算法的整合并不像 1 加 1 等于 2 那样简单,我们必须引入一些全新的思路。 1970 年前后, G. L. Steele , C. J. Cheney 和 D. S. Wise 等研究者陆续找到了正确的方向,标记-整理算法的轮廓也逐渐清晰了起来: 在我们熟悉的餐厅里,这一次,垃圾收集机器人不再把餐厅分成两个南北区域了。需要执行垃圾收集任务时,机器人先执行标记-清除算法的第一个步骤,为所有使用中的餐巾纸画好标记,然后,机器人命令所有就餐者带上有标记的餐巾纸向餐厅的南面集中,同时把没有标记的废旧餐巾纸扔向餐厅北面。这样一来,机器人只消站在餐厅北面,怀抱垃圾箱,迎接扑面而来的废旧餐巾纸就行了。 实验表明,标记-整理算法的总体执行效率高于标记-清除算法,又不像复制算法那样需要牺牲一半的存储空间,这显然是一种非常理想的结果。在许多现代的垃圾收集器中,人们都使用了标记-整理算法或其改进版本。 增量收集( Incremental Collecting )算法 对实时垃圾收集算法的研究直接导致了增量收集算法的诞生。 最初,人们关于实时垃圾收集的想法是这样的:为了进行实时的垃圾收集,可以设计一个多进程的运行环境,比如用一个进程执行垃圾收集工作,另一个进程执行程序代码。这样一来,垃圾收集工作看上去就仿佛是在后台悄悄完成的,不会打断程序代码的运行。 在收集餐巾纸的例子中,这一思路可以被理解为:垃圾收集机器人在人们用餐的同时寻找废弃的餐巾纸并将它们扔到垃圾箱里。这个看似简单的思路会在设计和实现时碰上进程间冲突的难题。比如说,如果垃圾收集进程包括标记和清除两个工作阶段,那么,垃圾收集器在第一阶段中辛辛苦苦标记出的结果很可能被另一个进程中的内存操作代码修改得面目全非,以至于第二阶段的工作没有办法开展。 M. L. Minsky 和 D. E. Knuth 对实时垃圾收集过程中的技术难点进行了早期的研究, G. L. Steele 于 1975 年发表了题为“多进程整理的垃圾收集( Multiprocessing compactifying garbage collection )”的论文,描述了一种被后人称为“ Minsky-Knuth-Steele 算法”的实时垃圾收集算法。 E. W. Dijkstra , L. Lamport , R. R. Fenichel 和 J. C. Yochelson 等人也相继在此领域做出了各自的贡献。 1978 年, H. G. Baker 发表了“串行计算机上的实时表处理技术( List Processing in Real Time on a Serial Computer )”一文,系统阐述了多进程环境下用于垃圾收集的增量收集算法。 增量收集算法的基础仍是传统的标记-清除和复制算法。增量收集算法通过对进程间冲突的妥善处理,允许垃圾收集进程以分阶段的方式完成标记、清理或复制工作。详细分析各种增量收集算法的内部机理是一件相当繁琐的事情,在这里,读者们需要了解的仅仅是: H. G. Baker 等人的努力已经将实时垃圾收集的梦想变成了现实,我们再也不用为垃圾收集打断程序的运行而烦恼了。 分代收集( Generational Collecting )算法 和大多数软件开发技术一样,统计学原理总能在技术发展的过程中起到强力催化剂的作用。 1980 年前后,善于在研究中使用统计分析知识的技术人员发现,大多数内存块的生存周期都比较短,垃圾收集器应当把更多的精力放在检查和清理新分配的内存块上。这个发现对于垃圾收集技术的价值可以用餐巾纸的例子概括如下: 如果垃圾收集机器人足够聪明,事先摸清了餐厅里每个人在用餐时使用餐巾纸的习惯——比如有些人喜欢在用餐前后各用掉一张餐巾纸,有的人喜欢自始至终攥着一张餐巾纸不放,有的人则每打一个喷嚏就用去一张餐巾纸——机器人就可以制定出更完善的餐巾纸回收计划,并总是在人们刚扔掉餐巾纸没多久就把垃圾捡走。这种基于统计学原理的做法当然可以让餐厅的整洁度成倍提高。 D. E. Knuth , T. Knight , G. Sussman 和 R. Stallman 等人对内存垃圾的分类处理做了最早的研究。 1983 年, H. Lieberman 和 C. Hewitt 发表了题为“基于对象寿命的一种实时垃圾收集器( A real-time garbage collector based on the lifetimes of objects )”的论文。这篇著名的论文标志着分代收集算法的正式诞生。此后,在 H. G. Baker , R. L. Hudson , J. E. B. Moss 等人的共同努力下,分代收集算法逐渐成为了垃圾收集领域里的主流技术。 分代收集算法通常将堆中的内存块按寿命分为两类,年老的和年轻的。垃圾收集器使用不同的收集算法或收集策略,分别处理这两类内存块,并特别地把主要工作时间花在处理年轻的内存块上。分代收集算法使垃圾收集器在有限的资源条件下,可以更为有效地工作——这种效率上的提高在今天的 Java 虚拟机中得到了最好的证明。 应用浪潮 Lisp 是垃圾收集技术的第一个受益者,但显然不是最后一个。在 Lisp 语言之后,许许多多传统的、现代的、后现代的语言已经把垃圾收集技术拉入了自己的怀抱。随便举几个例子吧:诞生于 1964 年的 Simula 语言, 1969 年的 Smalltalk 语言, 1970 年的 Prolog 语言, 1973 年的 ML 语言, 1975 年的 Scheme 语言, 1983 年的 Modula-3 语言, 1986 年的 Eiffel 语言, 1987 年的 Haskell 语言u2026u2026它们都先后使用了自动垃圾收集技术。当然,每一种语言使用的垃圾收集算法可能不尽相同,大多数语言和运行环境甚至同时使用了多种垃圾收集算法。但无论怎样,这些实例都说明,垃圾收集技术从诞生的那一天起就不是一种曲高和寡的“学院派”技术。 对于我们熟悉的 C 和 C++ 语言,垃圾收集技术一样可以发挥巨大的功效。正如我们在学校中就已经知道的那样, C 和 C++ 语言本身并没有提供垃圾收集机制,但这并不妨碍我们在程序中使用具有垃圾收集功能的函数库或类库。例如,早在 1988 年, H. J. Boehm 和 A. J. Demers 就成功地实现了一种使用保守垃圾收集算法( Conservative GC Algorithmic )的函数库。我们可以在 C 语言或 C++ 语言中使用该函数库完成自动垃圾收集功能,必要时,甚至还可以让传统的 C/C++ 代码与使用自动垃圾收集功能的 C/C++ 代码在一个程序里协同工作。 1995 年诞生的 Java 语言在一夜之间将垃圾收集技术变成了软件开发领域里最为流行的技术之一。从某种角度说,我们很难分清究竟是 Java 从垃圾收集中受益,还是垃圾收集技术本身借 Java 的普及而扬名。值得注意的是,不同版本的 Java 虚拟机使用的垃圾收集机制并不完全相同, Java 虚拟机其实也经过了一个从简单到复杂的发展过程。在 Java 虚拟机的 1.4.1 版中,人们可以体验到的垃圾收集算法就包括分代收集、复制收集、增量收集、标记-整理、并行复制( Parallel Copying )、并行清除( Parallel Scavenging )、并发( Concurrent )收集等许多种, Java 程序运行速度的不断提升在很大程度上应该归功于垃圾收集技术的发展与完善。 尽管历史上已经有许多包含垃圾收集技术的应用平台和 操作系统 出现,但 Microsoft .NET 却是第一种真正实用化的、包含了垃圾收集机制的通用语言运行环境。事实上, .NET 平台上的所有语言,包括 C# 、 Visual Basic .NET 、 Visual C++ .NET 、 J# 等等,都可以通过几乎完全相同的方式使用 .NET 平台提供的垃圾收集机制。我们似乎可以断言, .NET 是垃圾收集技术在应用领域里的一次重大变革,它使垃圾收集技术从一种单纯的技术变成了应用环境乃至操作系统中的一种内在 文化 。这种变革对未来软件开发技术的影响力也许要远远超过 .NET 平台本身的商业价值。 大势所趋 今天,致力于垃圾收集技术研究的人们仍在不懈努力,他们的研究方向包括分布式系统的垃圾收集、复杂事务环境下的垃圾收集、数据库等特定系统的垃圾收集等等。 但在程序员中间,仍有不少人对垃圾收集技术不屑一顾,他们宁愿相信自己逐行编写的 free 或 delete 命令,也不愿把垃圾收集的重任交给那些在他们看来既蠢又笨的垃圾收集器。 我个人认为,垃圾收集技术的普及是大势所趋,这就像生活会越来越好一样毋庸置疑。今天的程序员也许会因为垃圾收集器要占用一定的 CPU 资源而对其望而却步,但二十多年前的程序员还曾因为高级语言速度太慢而坚持用机器语言写程序呢!在硬件速度日新月异的今天,我们是要吝惜那一点儿时间损耗而踟躇不前,还是该坚定不移地站在代码和运行环境的净化剂——垃圾收集的一边呢?
DOS是怎样进行内存管理的
DOS是怎样进行内存管理的?内存是计算机中存储程序代码以及数据的地方,是程序赖以运行的舞台。内存在计算机中编址,形成了内存地址空间,既包括了RAM内存,也包括了ROM内存。通常的内存编址方法是按字节(8bit进行的,1M内存便是指大小为1兆字节的内存空间。以PC机为例,早期的8086/8088系统共有20根地址线,则可寻址2^20=1M字节;80286系统则有24根地址线,相应可寻址2^24=16M字节,到了386、486,地址总线宽度扩大到了32根,可寻址的内存空间也扩大为4G字节。内存地址空间又称为物理地址空间,它是实际主存储器的大小;相对有虚拟地址空间的概念,也称逻辑地址空间,它指的是应用程序的编级地址空间。二者有时等同,有时相互区别,这取决于系统的内存管理策略。无论是早期的DOS版本,还是最近的6.22;DOS下直接使用的都是物理地址空间。如果要使用逻辑地址空间,你必须自己编程。这一点,我们后面还要讲到。DOS是单用户单作业操作系统,因此采用的是固定分区内存管理技术,把内存低端部分(1M以下)固定地分成系统区和用户区(TPA),见图4-3。当前版本的MS—DOS可以管理1MB的物理地址空间。在IBMPC及其兼容机上,由MS-DOS和其他程序所占的内存在地址0000H处开始,并可达地址09FFFFH,此640KB的RAM区域有时称为“常规内存区”。此地址以上的内存区是为ROM硬件驱动程序、视频刷新缓冲区等所保留的。非IBM兼容机有可能使用其他内存区段。在MS—DOS控制下的RAM区可分成两个主要部分:操作系统区域临时程序区域(TPA)操作系统区域在地址0000H处开始,即它占用RAM的最低部分。该处有中断向量表、操作系统模块及其所拥有的表(table)和缓冲区,以及 config.sys)文件中指定的附加可安装设备驱动程序及command.com命令解释程序的驻留部分。操作系统区所占的内存量随MS-DOS版本、磁盘缓冲区数、可安装设备驱动程序个数与大小而改变。临时程序区为RAM中所存操作系统区上面的剩余部分,是动态可分配的内存。DOS将在应用程序运行时总是试图把这部分内存分配给它,必要时系统区1(参见图4—3)将腾出以扩大TPA区。如果此区过小,则会出现。out of memory内存不够的错误,你的应用程序将无法运行。 当出现此错误时,在低版本DOS中将是一筹莫展,在8086/8088中亦是如此,最多能做到的是不装载任何TSR程序。这意味着你甚至需要牺牲鼠标器支持;而高版本DOS则可以利用将要讲到的两点功能(1)把DOS自身一部分装载到HMA中,(2)把TSR程序和驱动程序装载到UMB,从而达到扩大 TPA,供应用程序运行的目的。对于1M以上的内存,DOS将无能为力,除了下面将要讲到HMA以外。================================================================343.为什么在386、486的计算机上虽然有4M或更多的内存配置,却总是会出现“内存不够”的错误?这正是DOS的局限性所带来的。从前面的问题中我们可以看出,在DOS中,真正供应用程序运行区域是TPA。DOS为了保持与8086/8088系统兼容性,把TPA限制在1M以下.所以有时计算机虽有4M或更多内存,你也无法直接使用。当然,1M以上的内存也还是可以利用的。(1)在保护模式下,你可以直接寻址1M以上空问。DOS是无法做到这一点,而Windows或OS/2等其他操作系统可以做到。(2)利用DOS提供的工具,间接访问1M以上空间,如EMS或XMS技术。关于EMS和XMS请参见《如何使用扩充内存(Expanded)》和《如何使用扩展内存Extended)》。===============================346.什么是常规内存、扩充内存、扩展内存?常规内存(Conventional Memory)是指从00000H到9FFFPH这640k的内存空间,它是DOS的传统势力范围.DOS对这一段的划分和管理请参阅《DOS是怎样进行内存管理的》。扩充内存(Expanded Memory)是一种不参与存储器统一统址的内存。其使用规范称作EMS(Expanded Memory Sepecfication),因为是由Lotus,Intel和Microsoft三家公司联合开发的,故也称作LIM EMS。早期XT机型上扩充内存以内存卡的物理形式出现,后来的高档微机上则可以由扩展内存(Extended Memory)来仿真扩充内存,这是CPU开始支持内存映射的结果。扩展内存是原1MB内存空间的纵向延伸.这种内存只存在于80286以上的机型之中。这种内存,只是在“保护(protected)”方式时是可寻址的; DOS运行于real方式(实模式),仿真8086寻址空间,因此标准的DOS应用程序是不能直接访问它的。对286来说,CPU共有24根地址线,其扩展内存可达15MB,而在386,486或更高档机器上。扩展内存可拥有4GB-1MB的大小,这是因为它们的地址总线宽度为32位。===============================347 如何使用扩充内存(Expanded Memory)?因为扩充内存没有统一编址,所以CPU不能象访问普通的主存那样通过地址线来直接访问它。这中间就需要扩充内存管理程序EMM,也既是软件技术的支持。对扩充内存的访问有些像日常去图书馆借书。你是没有权力进入书库象查检自己的书柜那样进行“访问”的,只有去到柜台那儿,交给管理员小姐您的索书单,她便进库去找寻您要的书,找到之后回来再为您办理手续——如果把书库看作您自家书柜这一主存的扩充内存的话,管理员小姐便是EMM,您便是CPU。柜台呢,则称作页框.页框的大小是64K,为什么是64K呢?您一定记得8086的段式访问机制,一个段的大小便是64k。这样,只要知道了页框起始处的段地址,就可以通过一个偏移量指针遍历整个页框,如果超过64K,那就需要调整段地址了.或许EMS的设计者考虑到内存的使用效率而把扩充内存以16k为单位进行组织划分,而不是一个页框的大小。现在EMS内中的每16k就称作一页,那么 CPU同时也就只能“看见”扩充内存其中四页,虽然程序可以申请到不止四页的扩充内存。这自然类似于图书馆的制度,您可以“借”图书馆的许多书,甚至全部的书,但一次您只能“惜到”十本书,看完归还之后再来借.编程中对扩充内存进行访问的步骤大致如下:(1)EMM安装了没有?无论是EMM386.EXE还是QEMM386.SYS等EMM程序,具有统一的设备名“EMMxxxx”,我们可以通过判断此设备是否安装来判断EMM存在与否?(2)页框起始地址在何处?这是判断EMM已安装后的第二个步骤。页框是我们“看到”EMS内存的窗口,当然首先要找到它。(3)申请EMS内存如果您估计您的程序数据量不会超过1MB,那就申请64页的扩充内存好了(当然要实际存在如此多的扩充内存才可以,EMM不可能无中生有),申请到的EMS内存由一个称作句柄的整数来标志管理.当然您可以随时根据需要多次申请EMS内存,只不过处理多个句柄总是要多些麻烦。(4)映射EMS内存申请到了一些EMS内存页面,意味着你可以“看”它们,但迄今为止它们并不“可见”,还需要进行映射。映射的意思,也就告诉EMM,让它把哪一个句柄的哪四页(少一些页自然可以),放到页框这个窗口中来。做完映射工作之后,您就可以往页框中存放,读取数据了.当这几页用完之后.再映射其它几页到页框中,而原来的四页则不再‘可见"。如此这般,您就可以把您大量的数据存放到EMS内存中。(5)释放EMS内存当工作完成之后,还需要把申请到的EMS内存加以释放,因为EMM无法判断程序的结束而主动地来完成这项工作。这一系列步骤使得EMS内存的使用看起来有些麻烦。的确,EMS内存的使用在一定程度上降低了系统性能.但这是值得的,要知道世界上没有免费的午餐.。。。。。。================================== 348.如何使用扩展内存(Extended Memory)?早期在DOS下访问扩展内存,必须通过BIOS中断INT l5H,但是因为该接口功能不够严谨,各软件之间容易因为使用扩展内存而发生冲突.有鉴于此,1988年底Microsoft,Intel,Lotus和AST四家公司共同开发出了关于存取扩展内存的标准,这就是扩展内存规范XMS(Extended Memory Specification).为了兼容性,大多数XMS驱动程序都可以为INT l5H保留一席之地.DOS及DOS应用程序无法直接访问扩展内存,必须通过XMS功能接口达到目的.XMS编程大致可分以下几个步骤:(1)判断XMS是否已安装这一点可以通过调用INT 2FH的4300H 号子功能来实现,通常INT 2FH以其“假脱机打印”功能而闻名。功能调用之后,若寄存器AL=80H 表示XMS驱动程序已安装,反之则否.(2)申请XMS内存块 "XMS编程接口有一点很特殊,不同于一般的中断调用,就是各子功能的执行是要对XMS驱动程序入口点进行远调用,而不是中断调用.XMS驱动程序的入口地址可以通过INT 2FH的子功能4310H号来得到.调用完毕后,ES:BX即为入口点.。。。。。。(3)数据的存取申请到了扩展内存,但我们并不能直接使用它,仍需要先把数据存放在一个常规内存缓冲区内,待恰当时机再把数据从缓冲区移到扩展内存之中.这一点对应XMS的0BH 号子功能:移动扩展内存块。(4)释放扩展内存该子功能号为0AH,入口参数为要释放的扩展内存块的句柄.对于上面的讨论,请进一步阅读有关资料,以得到更详尽的信息.下面给出一个完整的例子,供你参考。。。。。。。==================================349.什么是UMB? 如何使用UMB?640K~1MB这一段地址空间是微机的设计者保留下来给系统以及外部扩展设备来使用的,因此在这一段之中没有系统板上的RAM.如果您的机器配置了 1MB的内存,并且机器没有打开明SHADOW功能的话,将会有384k的扩展内存,此时RAM最高地址为1408K。这一段地址空间某些部分的分配是比较固定的:。。。。。。其他部分的地址空间则没有固定地被占用,由其他硬件扩展设备,如汉卡、网卡等等来选择.一般情况下,这些空闲地址空间可用来再生成上位内存块UMB,供DOS利用.让我们来看一个VGA配置的PC兼容机有多大的空闲空间。首先B000—B7FF这一段32k的空闲,再就是从C800开始到EFFF结束的这一段,大小为160k——所以我们总共可以有192KB的空间提供给DOS。如果再算上HMA 64K的话,就意味着DOS拥有了896k=(640+192+64)K 的空间!事情总要打点折扣,首先如果你安装了网卡,便只有牺牲16K的大小(通常情况);如果您的应用程序需要扩充内存,或者通过扩充内存卡获得,或者通过 EMM386.exe等类似的EMM程序利用扩展内存来仿真得到,但都必须在640k--1MB这一段的空闲区内生成一个EMS页框,又会占去64K的大小.另外一个问题是保留内存中的空闲区并不连续,在插入其他插卡后有可能被再次分割.如果一个网卡占用了E000~E3FF这16K,原选160k的连续空闲区将被分割为两块,一块96K,一块48K。这就使得DOS在把驱动程序、内存驻留程序装载到UMB运行时,不可避免地造成空间浪费,不能充分利用.这儿是用MSD察看保留内存区的一个例子。。。。。。。保留内存区又称上位内存区UMA,所以利用其间的空闲地址再生成的RAM块被称作UMB,也就是上位内存块的意思。为了使DOS能够利用UMB,仅仅在CONFIG.SYS中加载EMM386.exe之类的UMB Provider程序还是不够的,必须再通过命令DOS=UMB在二者之间建立起联系,这样DOS的DEVICEHIGH、LH命令才起作用,DOS的中断功能调用也才能够访问UMB。如果没有在CONFIG中运行DOS=UMB,则利用MSD、MEM等实用程序将无法看到UMB的影子,但此时应用程序可以直接向UMB Provider程序申请使用UMB。==================================350,什么是HMA?如何使用HMA?高位内存区HMA可能是最近人摸不着头脑的东西,它是1MB以上的第一个64K,其实要少16个字节,也就是65520字节的大小.这一段可以为DOS直接访问。不是说DOS只能直接访问1MB以下的内存区域吗? 我们已知道,DOS的寻址方式是8086/8088的段式机制,16位段地址左移四位再加上16位的偏移量形成20位的地址。这样问题便来了,1MB的绝对地址可以写成段:偏移量的形成为FFFF:000FH,也就是该段起于1MB空间的最后一小节FFFF0H 处;如图4-4所示,很明显,该段还有一部分的空间在1MB以上,也就是扩展内存之内.为FFFF:000FH只是该段的第一个小节结束的地方;偏移量还有FFF0的“余量”, 这“余量”确定的空间显然可以由DOS直接访问,只得把编移量累加上去就行了.该段的1MB以上区域便是HMA,很显然其大小为64K-16B.从绝对地址的角度看,HMA的第一个字节地址FFFF:0010H 已是21位地址宽度:1000000H.这在8086/8088中将产生地址绕回错误,因为8086/8088只有20根地址线,第21根子虚乌有,所以1000000H 处即为0000OH 处.这样,“HMA”便成了0000:0000H至0OOO:FFEFH这一段!早期的一些应用程序利用这一点来作为特殊技巧,达到所需的编程效果,也就是说,它们依赖于地址回绕.所以,到了后来的286、386等机器,虽然CPU拥有了24根、32根的地址总线,解决了8086/8088中16位地址进位超出总线宽度的问题,却为了保持兼容性,并没有把HMA区直接向DOS开放,而是通过—个“开关”进行控制.这个“开关”便是我们常常见到的A20 line。A20便是第21根地址线,众所周知,计算机中的总线都是从0开始编号的.DOS下HMA区的管理由HIMEM.SYS来提供。程序可以控制A20 line的开、关,可以申请使用HMA,但有一点,HMA不能分割使用,也就是说一次只能有一个程序在其间运行.如果在CONFIG.SYS文件中加载了 HIMEM.SYS驱动程序并且同时有DOS=high命令时,MS-DOS将使A20线有效,并为HMA直新定位系统代码.。。。。。。笔者建议在编程中不要试图去使用HMA,免得遇到很多不必要的麻烦,利用HMA的最理想方法便是通过在CONFIG.SYS中加载HIMEM.SYS并运行DOS=high命令,把MS-DOS系统代码加载至其中,从而腾出更多的常规内存来.在DOS 6.2版本中,DOS=high命令可腾出约49K的空间出来.关于HMA的使用和编程,请进一步阅读有关的详细资料.=============================351.如何使用HIMEM.SYS?HIMEM.SYS是一扩展(Extended)内存管理程序,也就是常说的XMM,它为扩展内存的使用提供了统一接口标准XMS,避免了早期扩展内存使用当中存在的冲突问题;同时HIMEM.SYS还提供对HMA的管理.HIMEM.SYS必须在CONFIG.SYS中首先加载,这一点很容易理解,在EMM386.EXE加载之后,保留内存区持有UMB出现, HIMEM.SYS也提供对UMB进行管理的接口.如果在CONFIG.SYS没有运行DOS=UMB命令,也即没有建立起DOS和UMB之间的联系时,应用程序可以直接向HIMEM.SYS申请使用UMB.============================= 352.如何使用EMM386.EXE?EMM386.EXE利用扩展(Extended)内存来仿真扩充(Expanded)内存,以提供对需要扩充内存的应用程序的支持,同时它还提供UMB功能,使得一些设备驱动程序和内存驻留程序能够加载至UMB中,从而腾出更多的常规内存。EMM386.EXE在CONFIG.SYS中必须位于HIMEM.SYS之后来加载,因为它依赖于HIMEM.SYS的扩展内存支持。这二者也都必须使用DEVICE来加载,而不是DEVICEHIGH,这是因为DEVICEHIGH依赖于EMM386.EXE提供的UMB功能。同理,要在AUTOEXEC.BAT中使用LOADHIGH(LH)命令也必须在CONFIG.SYS中已加载EMM386.EXE前提之下,并使用DOS=UMB命令在DOS和UMB之间建立起联系。
内存管理有哪些主要功能?其主要任务是什么?
内存管理主要是存储视频或者文件,而主要功能主要是缓解储存空间的不足,或者让运行更快。
内存管理的基本问题
内存管理是指软件运行时对计算机内存资源的分配和使用的技术。其最主要的目的是如何高效,快速的分配,并且在适当的时候释放和回收内存资源。一个执行中的程式,譬如网页浏览器在个人电脑或是图灵机(Turing machine)里面,为一个行程将资料转换于真实世界及电脑内存之间,然后将资料存于电脑内存内部(在计算机科学,一个程式是一群指令的集合,一个行程是电脑在执行中的程式)。一个程式结构由以下两部分而成:“本文区段”,也就是指令存放,提供CPU使用及执行; “资料区段”,储存程式内部本身设定的资料,例如常数字串。技术简介内存可以通过许多媒介实现,例如磁带或是磁盘,或是小阵列容量的微芯片。 从1950年代开始,计算机变的更复杂,它内部由许多种类的内存组成。内存管理的任务也变的更加复杂,甚至必须在一台机器同时执行多个进程。虚拟内存是内存管理技术的一个极其实用的创新。它是一段程序(由操作系统调度),持续监控着所有物理内存中的代码段、数据段,并保证他们在运行中的效率以及可靠性,对于每个用户层(user-level)的进程分配一段虚拟内存空间。当进程建立时,不需要在物理内存件之间搬移数据,数据储存于磁盘内的虚拟内存空间,也不需要为该进程去配置主内存空间,只有当该进程被被调用的时候才会被加载到主内存。可以想像一个很大的程序,当他执行时被操作系统调用,其运行需要的内存数据都被存到磁盘内的虚拟内存,只有需要用到的部分才被加载到主内存内部运行。
操作系统内存管理发展史
内存是计算机很重要的一个资源,因为程序只有被加载到内存中才可以运行;此外,CPU所需要的指令与数据也都是来自内存的。可以说,内存是影响计算机性能的一个很重要的因素。 随着技术的发展,现在计算机的内存容量已经有了很大的增长,但是程序大小的增长速度比内存容量的增长速度要快得多。正如帕金森定律所指出,“不论存储器有多大,程序都可以把它填满”。所以问题来了,当一个程序大小超过内存容量时,如何把调入内存中?这篇文章将总结内存管理的一些技术。 在介绍内存管理的细节前,先要了解一下分层存储器体系(一图胜千言)。分层存储体系这个模型并不是一开始就有的,而是随着计算机的发展来一步步完善,最终才形成了现在的体系。所以,分层存储体系的发展历史也是这篇文章的一个线索。 早期的计算机是没有存储器抽象的,直接将物理内存暴露给程序。可以把内存想象成中医中放草药的盒子,每个盒子都有标记放的是哪种草药,当有两种草药同时放到一个盒子中去,就会出现医疗事故。 所以,这种情况下的内存管理是有问题的: 为了解决保护和重定位的问题,人们创造了一个新的内存抽象:地址空间。地址空间是一个进程可用于寻址内存的一套地址集合。每个进程都有一个自己的地址空间,并且这个地址空间独立于其他的地址空间。 地址空间在现实生活中有多的应用。比如电话的区号,北京是010,上海是021。这样即使同一个电话号码,区号不同也能区分开来。 考虑上个例子中的程序A、B,如果使用地址空间的话,B程序跳转28,就不会跳转到A程序的指令了。因为两个28分别属于不同地址空间。 了解地址空间这个概念,接下来看一下计算机是如何实现将两个28映射到不同地址空间里的。 经典的办法是给每个CPU配置两个寄存---基址寄存器和界限寄存器。基址寄存器记录程序的起始物理地址,界限寄存器记录程序的长度。 还是用这个图举例,程序A的基址为0,界限为16384,程序B基址为16384,界限值为32768。每次一个进程访问内存时,取一条指令,读或写一个数据字,CPU硬件会把地址发送到内存总线前自动把基址值加到进程发出的地址上。比如,进程A发出的访问地址是28,而A的基址是0,那么,A实际访问的物理地址就是28,而进程B的基址为16384,加上28,得到的地址为16412,进程B实际访问的地址是16412.这样就解决了之前提到过的绝对物理地址所带来的问题。 按照帕金森定律,“不论存储器有多大,程序都可以把它填满”。当程序大小超过内存容量时,计算机应该如何管理内存? 有两种处理内存超载的通用方法。一种是交换技术,即把一个进程完整调入内存,使该进程运行一段数据啊in,然后存回磁盘。空闲进程主要存在于磁盘上,所以,当他们不运行的时候不会占用内存。另一种是虚拟内存,该策略允许程序在只有一部分被调入内存的情况下运行。 我们重点关注虚拟内存。大体包含以下内容: 虚拟内存基本思想是每个程序都有自己的地址空间,这个空间被分割成多个块。每个块称作一页或页面。每一页有连续的地址范围。这些页被映射到物理内存,但是并不是所有的页都必须在内存中才能运行程序。当程序引用到一部分在物理内存中的地址空间时,由硬件执行必要的映射。当程序引用到一部分不在物理内存中的的地址空间时,由操作系统负责将缺失的部分装入物理内存并重新执行失败的指令。 至此,对操作系统中的内存管理有了一个大体的了解,如内存模型对象的发展过程,每种内存管理的优缺点。同时我感受到了提出问题,解决问题这条线索也是阅读计算机书籍的一个思路。
内存管理的功能主要体现在哪些方面?
分配、收回。
Python如何进行内存管理
Python是如何进行内存管理的?答:从三个方面来说,一对象的引用计数机制,二垃圾回收机制,三内存池机制。一、对象的引用计数机制Python内部使用引用计数,来保持追踪内存中的对象,所有对象都有引用计数。引用计数增加的情况:1,一个对象分配一个新名称2,将其放入一个容器中(如列表、元组或字典)引用计数减少的情况:1,使用del语句对对象别名显示的销毁2,引用超出作用域或被重新赋值Sys.getrefcount( )函数可以获得对象的当前引用计数多数情况下,引用计数比你猜测得要大得多。对于不可变数据(如数字和字符串),解释器会在程序的不同部分共享内存,以便节约内存。相关推荐:《Python视频教程》二、垃圾回收1,当一个对象的引用计数归零时,它将被垃圾收集机制处理掉。2,当两个对象a和b相互引用时,del语句可以减少a和b的引用计数,并销毁用于引用底层对象的名称。然而由于每个对象都包含一个对其他对象的应用,因此引用计数不会归零,对象也不会销毁。(从而导致内存泄露)。为解决这一问题,解释器会定期执行一个循环检测器,搜索不可访问对象的循环并删除它们。三、内存池机制Python提供了对内存的垃圾收集机制,但是它将不用的内存放到内存池而不是返回给操作系统。1,Pymalloc机制。为了加速Python的执行效率,Python引入了一个内存池机制,用于管理对小块内存的申请和释放。2,Python中所有小于256个字节的对象都使用pymalloc实现的分配器,而大的对象则使用系统的malloc。3,对于Python对象,如整数,浮点数和List,都有其独立的私有内存池,对象间不共享他们的内存池。也就是说如果你分配又释放了大量的整数,用于缓存这些整数的内存就不能再分配给浮点数。
内存管理器如何跟踪哪些进程正在使用操作系统中的哪些内存区域?
1、首先,让我们打开任务管理器。右键单击任务栏,在弹出的菜单中选择“任务管理器”。2、其次在任务管理器界面,单击“更多详细信息”按钮。3、最后在任务管理器窗口中,导航到“进程”选项卡,点击“内存”列标题即可查看正在使用操作系统中的内存区。
内存管理有哪些主要功能?其主要任务是什么?
具体说说如下。1.你的可爱,治愈一切不可爱。2.你是爸爸妈妈不期而遇的温暖。3.愿时光能缓,愿你赤忱善良。4.谢谢你让我每天都元气满满。5.你总能成为我热爱生活的原因。6.我牵着你,你牵着我,长长的路陪你慢慢走。7.用心记录你生命里每个不可复制的瞬间。8.辛苦是幸福的、熬夜是值得的。9.我的岁月因你而暖,因你而乐。10.孩子是天生的表演。11.日夜相对,仍百处不腻。12.你就是上天赐给我最好的礼物。13.期待你长大以后的样子。14.你是妈妈的巧克力乖巧刻骨又努力。15.你成长的道路千姿百态,呆萌、可爱、调皮又搞怪。16.抱着软绵绵的你像是抱住了整个世界。17.你给我的甜,早已经多过了给我的苦。18.因为有了你的存在,欢声笑语不断、快乐幸福涌入19.终有一天你也会像个公主,所有梦都被满足。