虚拟内存管理范文

2024-06-16

虚拟内存管理范文(精选7篇)

虚拟内存管理 第1篇

Java语言的面向对象,平台无关,安全,开发效率高等特点,使其在许多领域中得到了越来越广泛的应用,但是由于Java程序由于自身的局限性,使其无法应用于实时领域。Java程序是运行在Java虚拟机上,而Java虚拟机是计算机体系结构的一个软件实现,由软件模拟来实现各个Java指令解释和执行,这在一定程度上影响了其性能[1]。由于内存管理对Java虚拟机的实时性具有非常大的影响,通过深入研究Java虚拟机的内存管理机制,改善其内存管理的实时性,对于实现Java虚拟机整体的高效率、高可靠性、高可预测性都具有很大的实际意义。

2. 实时JAVA虚拟机的框架设计

实时Java虚拟机可以说是实时系统的一个实例,不过它是一个运行于操作系统和硬件平台之上的实时系统。实时Java虚拟机要保证在其中运行的Java程序具有实时特性,而它自身又是运行于操作系统之上,其实时性很大程度上依赖于该宿主操作系统所提供服务的实时性。因此要实现实时Java虚拟机,需要有实时操作系统的支持[2]。实时Java虚拟机系统框图见图2-1所示。

3. 实时JAVA虚拟机的内存管理

3.1 内存区域的模型设计

内存区域 (Memory Area) 的基本模型如图3-1所示。内存区域信息包含了一个一个指针指向其所维护的内存空间,因此通过内存区域信息就可以找到内存空间。同时内存区域信息是有层级关系的,每个内存区域可以包括多个子内存区域。

对于任何一个Java对象来说,它都包含了两个部分,一部分是由Java语言实现的Java类,另一部分是其在Java虚拟机内部对应的数据结构。Java的执行引擎执行Java类中的程序,同时维护其对应的虚拟机内部的数据结构。由于Java程序的运行是解释型的,显然维护虚拟机数据结构比维护Java数据结构更加高效,且更具有确定性。要提高Java程序的实时性,就要把Java类中核心部分更多的由虚拟机来维护和处理。

在内存空间中的对象都是Java对象实例,这些对象可分为普通对象和内存区域对象。内存区域对象也是一个普通的Java对象,它不仅可以分配在堆区域内,还可以分配在其它任何的内存区域内。内存区域的数据结构定义如下:

每个内存区域都由其父内存区域来管理,即由父内存区域来决定其分配和释放的方法。每个父内存区域内部都维护着其第一层子内存区域的链表,这样层层的父子关系就构成了一棵树。因为虚拟机刚启动的时,对象都是创建在堆中的,所以堆内存区域必然是所有内存区域的父亲。

3.2 内存区域的堆栈管理

内存区域需要在Java虚拟机中良好的管理,分配和释放。区别于传统的Java虚拟机所有的线程都只使用唯一的一个内存区域——堆,实时Java虚拟机的多个生命期不同的内存区域是由各个使用它的线程中的领域堆栈 (Scope Stack) 来管理的。检查对象引用规则时需要领域堆栈来保存区域内存嵌套关系,每个线程有自己的领域堆栈。在创建一个实时线程时,领域堆栈初始化如下:

如果该线程是在区域内存中创建,则它的初始领域堆栈包含了其创建时双亲线程领域堆栈结构的副本;如果该线程在堆或永久内存中创建,它的领域堆栈结构初始化为只包含堆或永久内存。之后随着线程的推移,领域堆栈不断地把区域内存压入或弹出,保存它们嵌套使用的层次关系。

实时JAVA虚拟机提供了三种方法来进入一个区域内存,此后的空间将在此区域进行分配,直到退出该区域或进入另一个区域。

New thread:在创建一个线程时,把一个区域内存 (ma) 句柄作为参数传递给该线程,该线程默认的空间分配将在该区域内存中进行。

enter:当每次调用ma.enter () 方法时,把ma压入该线程的领域堆栈。同时该线程默认的空间分配将在ma进行,直到enter () 方法返回,或进入另一个区域。

executeInArea:用法和enter () 类似,区别是该线程的领域堆栈不变,只是使用已在领域堆栈中的区域内存。

3.3 内存分配的时间管理

针对内存分配时间的问题,实时JAVA虚拟机的内存管理机制中的Scoped Memory可以派生出3个子类CTMemoy, LTMemory, VTMemory,内存管理机制对它们分配对象的时间有不同的要求。CTMemory要求在其中分配对象的时间是常数时间,LTMemory要求在其中分配对象的时间与对象的大小成正比,VTMemory要求在其中分配对象的时间是任意的,由于没有限制,它更关注于在空间上高效的分配内存。

此外,由于新增的内存区域中的对象可能会引用堆中的对象,因此垃圾收集的过程中也需要扫描新增内存区域中对象的字段,不过不会回收新增内存区域中的对象。

3.4 对象引用的规则管理

由于内存区域的生命期的不同,有些内存区域的生命期与Java虚拟机的生命期相同如Heap Memory和Immortal Memory,而有的生命期是短暂的,与操作它的线程相关,如Scoped Memory。这就引发了内存区域间的引用关系的问题。总的来说,生命期长的内存区域中的对象的字段不能引用生命期短的内存区域中的对象,因为当后者被销毁的时候,而前者由于继续存活而变成非法引用,这就要求建立一个合理而高效的内存引用检查机制。内存区域间的引用关系要求如表3-1所示。

为了方便多个线程共享领域内存时对象引用规则的检查,实时JAVA虚拟机的内存管理提出单亲规则,如果单亲规则不成立,对象引用规则一定不成立。单亲规则是指:每次往领域堆栈 (Scope Stack) 加入一个区域时都要确保它只有一个双亲。双亲是领域堆栈 (Scope Stack) 上相邻的外层领域内存、堆或不朽内存。

如图3-2,线程T1在内存区域Ma3中创建了一个对象O1,在Ma1中保存了O1的引用变量R1。线程T2在Ma2中创建了一个对象O2,在Ma1中保存了O2的引用变量R2。单独就T1和T2来说,都符合对象引用规则。但图3-2中,Ma1的双亲分别是Ma3和Ma2,违犯了单亲规则。假设T1线程退出Ma3后,没有其它线程使用,Ma3被释放掉。这时Ma1中的R1就成了悬空引用。所以在多线程共享区域内存的情况下,首先要保证单亲规则成立,再进一步针对每个线程检查对象引用规则。

4. 总结

总之,Java虚拟机内存管理在很大程度上依赖于Java虚拟机的实现,不同的Java虚拟机的实现对于其内存管理的性能也有一定的影响。但是其理论研究归纳起来主要有两个方面:一是对于领域内存的分配和释放问题,要求具有可预测性和高效性等;二是内存引用检查机制的效率问题,由于Scoped Memory的引入,要求在其中的对象的引用要遵循一定的规则。

摘要:改善JAVA虚拟机的内存管理对于系统的性能影响重大。本文在明确内存管理重要性的基础上, 分析了实时JAVA虚拟机的框架设计, 并详细研究了实时JAVA虚拟机的内存管理, 涉及到:内存区域的模型、内存区域的堆栈管理以及规则管理等。

关键词:实时系统,虚拟机,内存管理

参考文献

[1]南兆阔, 须文波, 柴志雷.一种实时JAVA处理器的数据通路研究[J].微计算机信息, 2008, (29) .

系统总提示虚拟内存低等 第2篇

A这主要是因为你的物理内存小,在运行一些大型软件时因为内存不够而试图调用虚拟内存,但虚拟内存设置又过小所致。同时还会导致软件运行速度明显变慢。可手工调整虚拟内存大小:右击“计算机”并选择“属性”,在打开的窗口中选择“高级”选项卡,单击“性能”选项卡中的“设置”按钮,在打开的窗口中选择“高级”选项卡,单击“更改”按钮将虚拟内存值设置大一些即可(见图1)。

傻博士有话说:

虚拟内存是对物理内存的补充,现在内存条价格低、容量大,如果物理内存特别大,平时运行的程序根本使用不完,这种情况下可以考虑禁用虚拟内存以提速。因为虚拟内存实际上就是在硬盘上模拟出的内存区域,它的读写速度是无法与物理内存相比的。所以有条件的话,建议升级物理内存以提高系统性能。(来源:http://bbs.cfan.com.cn/thread-1092655-1-1.html)

Win7玩游戏不卡WinXP却卡

Q我的电脑配置是Inter(R) Core(TM)2 E8200 CPU,4GB内存,安装Win7系统后可以正常玩的游戏,再改装WinXP系统后却很卡。请问这是什么原因?

A这一般与硬件设备(主要是显卡)的驱动程序有关系,现在Win7系统可以自动检索硬件设备信息并从Internet上搜索并下载安装最匹配的驱动程序,而WinXP系统却没有此功能。而你安装的显卡驱动程序在WinXP系统下如果不能很好地工作就会导致游戏运行卡的现象。建议搜索下载新版驱动试试,再检查一下DirectX组件是否为最新,使用旧版本的DirectX组件也有可能会出现游戏卡的现象。(来源:http://bbs.cfan.com.cn/thread-1092686-1-1.html)

光驱总显示前张光盘内容

QWinXP系统,最近不知道什么原因,即使更换了光盘但光驱总显示前一张光盘的内容。请问这该如何解决?

A这主要是系统中的“延迟卸除”功能所致,只要关闭该功能即可:右击“我的电脑”并选择“管理”,找到“存储→可移动存储→库”,找到光驱并右击选择“属性”,在打开的对话框中将“启用驱动器”选中,再将“延迟卸除”时间改为0分钟即可(见图2)。(来源:http://bbs.cfan.com.cn/thread-1091807-1-2.html)

组策略设置不能保存

Q我的是WinXP系统,在组策略编辑器中设置“网络连接”后面的“禁止访问LAN连接的属性”等几项策略时,重启后发现修改选项没有保存。请问这是什么原因?

A首先,按Win+R键调出运行对话框,输入“services.msc”(不含外侧引号)并按回车键,在打开“服务”窗口,找到并启动“TCP/IP NetBIOS Helper”服务。其次,再检查一下组策略中的“为管理员启用Windows 2000网络连接配置”策略是否启用,如果关闭请手工启用该策略。应该能够解决这一问题。

找出丢失的音量图标

QWin7系统,声音等一切正常,但是在托盘区找不到音量调节图标,使用很不方便。请问这该如何解决?

A这可能是音量图标被隐藏所致:展开托盘区扩展面板并单击“自定义”链接,在打开的窗口中单击“打开或关闭系统图标”,在列表中将“音量”设置为“打开”即可(见图3)。

系统不能自动关机

Q重新安装WinXP系统后,发出关机命令,等半天屏幕上才显示“你可以安全的关机了?”,而以前是可以正常关闭的啊。请问这该如何解决?

虚拟内存管理 第3篇

随着嵌入式系统得到越来越广泛的应用, 软件的功能和结构也日益复杂;因而在有限硬件资源的条件下, 尤其是在较少内存的情况下, 系统应具有高效、可靠的内存访问性能, 这对嵌入式系统的内存管理机制提出了更高的要求[1]。作为嵌入式系统的核心部分, 内存管理机制主要有两种:基于物理内存的管理机制和基于虚拟内存的管理机制, 前者直接对物理内存进行划分和操作, 系统可使用的地址空间不能超过物理内存的大小, 对软件系统的限制较大;而采用虚拟内存的管理机制, 通过内存映射实现虚拟地址与物理地址的转换、通过页面调度使虚拟内存具有更大的地址空间, 可为软件系统提供更宽余的内存访问性能。通过借鉴VxWorks操作系统的内核设计思想, 本文提出一个轻量级的、具有良好兼容性的虚拟内存管理机制。

1 对 VxWorks的内存管理机制的分析

1.1 VxWorks的内存管理

一般的嵌入式系统均不采用通用操作系统中复杂而完善的内存管理策略, 而取而代之以简单、快速的内存分配方案, 以实现内存快速、可靠、高效地分配[2]。VxWorks采用用户程序和内核处于同一地址空间的内存管理策略, 由内核负责为用户程序分配内存、动态分配内存和回收内存。VxWork为用户提供两种内存区域:可变长的内存域 (region) 和定长的内存分区 (partition) , 内存域使用灵活, 不浪费, 但容易产生碎片;内存分区无碎片, 效率高, 但浪费。通常, VxWorks内核和用户程序对内存的操作是基于内存分区进行的, 将整个系统内存 (system partition) 看作一个分区, 分区里含有一个或多个内存池 (pool) , 内存池里含有一个或多个内存块 (block) , 采用最先匹配算法实现内存块的分配、管理和释放, 用户也可使用系统提供的函数实现和管理自己的分区[3]。

VxWorks映像在RAM中运行后, 其内存布局如图1所示, 由图可知系统内存位于FREE_RAM_ADRS (或FREE_RAM_ADRS+ WDB_POOL_SIZE, 视是否定义了WDB代理而定) 到sysMemTop () 之间, 用户程序中动态申请的内存就位于这一空间中[4]。

1.2 VxWorks的虚拟内存

虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用的内存 (一个连续完整的地址空间) , 而实际上, 它通常是被分隔成多个物理内存碎片, 还有部分暂时存储在外部磁盘存储器上, 这部分数据只在需要的时候才被交换进物理内存, 这种交换是操作系统经由内存管理单元完成的, 物理内存和外围存储器之间的数据交换以页 (page) 为单位进行。

内存管理单元 (Memory Management Unit, MMU) 是中央处理器 (CPU) 中用来管理虚拟存储器、物理存储器的控制线路, 同时也负责虚拟地址映射为物理地址, 以及提供硬件机制的内存访问授权。对于带有MMU的目标系统, VxWorks提供两级虚拟内存的支持。基本级与VxWorks捆绑在一起, 提供标记不可cache的缓冲区的能力;完全级需要另外购买组件VxVMI来实现, 提供设置文本段和中断向量表为只读的能力, 并且包括一组用于开发人员管理他们自己的虚拟内存上下文的子程序。

出于成本的考虑, 在很多嵌入式系统中内存只是一种很有限的资源, 另一方面, 即使不考虑成本的因素, 系统硬件环境有限的空间和板面积也决定了可配置的内存容量是相当有限的。随着软件功能和结构的日益复杂, 对于不带有MMU的目标系统, 若配置的内存容量不能满足用户程序的需要而又未采取相应的应对机制, 就会影响系统的可靠性, 甚至造成不可预见的后果。因此, 在此类系统中需要提供一种虚拟内存管理方案, 以满足用户程序对大容量内存的使用需求。

2 嵌入式系统虚拟内存机制的设计

2.1 设计思想

借鉴通用操作系统的内存管理机制, 将虚拟内存技术和分页技术相结合, 以页表作为管理结构, 页面作为管理基础, 采用最近最少使用算法, 实现内存申请与销毁、分配与回收的实时管理[5]。

如图2所示, 在系统内存中保存一张页表, 用户程序中动态申请的每一块内存都对应页表中一个表项, 在系统的外存中保存与各表项一一对应的映像文件, 程序运行时只将所需的表项调入内存 (物理内存) , 其余表项存放在外存的映像文件 (虚拟内存) 中, 这样就可满足用户程序对大容量内存的使用需求[6]。

嵌入式处理器的地址转换过程大都通过多级地址转换表来完成, 整个虚拟内存的空间就是所有地址转换表所对应的虚拟页的容量总和。若只采用一级页表如图2所示, 页表中共有1 024个表项, 每个页表项对应的虚拟页大小为32 KB, 则用户可使用的虚拟内存空间可达到32 MB。

设计四个方法:Alloc、Free、Lock和UnLock。用户申请内存块时, 用Alloc方法实现页表项的分配;释放内存块时, 用Free方法实现页表项的销毁;内存块的分配和回收则通过Lock方法和UnLock方法来实现。

2.2 程序实现

2.2.1 数据结构

对页表的管理采用如下的数据结构进行:

typedef struct ask_table

{

BOOL alloc_flag; //页表项的分配标志, 当该表项被分配时置为TRUE, 释放时恢复为FALSE;

unsigned int serial_num; //块序号, 各内存块的唯一标识号, 从0开始递增的一个常数, 对内存块的分配、回收等操作均通过此标识号进行;

unsigned int ask_len; //用户申请的内存块大小;

unsigned int use_count; //内存块的使用频繁度标记, 每发生一次内存块的操作 (分配或回收) 其对应的该值即自加一次;

BOOL write_flag; //内存块的写标志, 当该内存块从内存写到外存时该值置为TRUE, 当从外存读回内存时置为FALSE;

char *pAddr; //内存块申请到的物理内存起始地址

} ASK_TABLE;

2.2.2 Alloc方法

Alloc方法实现页表项的分配, 当用户新申请一块内存时, 查询页表为其分配一个可用的表项, 置该表项的分配标志为TRUE, 登记内存块的大小, 同时登记块序号, 以后对该块内存的所有操作均通过此块序号进行。

int Alloc (unsigned int ask_len)

{

……

alloc_pos = findAllocPos () ; //alloc a item

phy_table[alloc_pos].ask_len = ask_len;

phy_table[alloc_pos].alloc_flag = TRUE;

……

return phy_table[alloc_pos].serial_num;

}

2.2.3 Free方法

Free方法实现页表项的销毁, 当用户最终释放某块内存时, 将其对应的页表项中除块序号之外的信息清除, 将分配标志重置为FALSE, 即可实现页表项的重用。

void Free (int serial_num)

{

phy_table[serial_num].ask_len = 0;

phy_table[serial_num].alloc_flag = FALSE;

……

}

2.2.4 Lock方法

Lock方法实现用户内存块的分配, 当用户分配某块内存时, 首先检查该块内存是否已经分配过, 若没有分配过则先为其分配物理内存;然后检查该块内存是否被交换到外存的映像文件中, 若是则将其交换回内存供用户调用;最后修改该块内存的使用频繁度标记。

在分配物理内存时, 首先检查系统中剩余的物理内存容量能否满足此次分配的需要, 若能够满足则直接分配所需容量的物理内存, 若不能满足则先将足够数量的内存块交换到其对应的外存映像文件上 (页面置换) , 释放足够的物理内存后再进行此次内存的分配。Lock方法的流程如图3所示。

页面置换的算法有很多, 有先进先出算法、最近最少使用算法、最近最不常用算法等, 本文采用最近最少使用 (Lease Recently Used, LRU) 算法实现, 它的思想是:如果某一块内存被访问, 则它很可能马上又被访问, 反之若某一块内存很长时间没有被访问, 那它最近也不大可能被访问到。因此, 在内存的分配过程中需要换出某些内存块时, 优先置换最近一段时间内被最少访问的那些内存块。LRU算法的流程如图4所示。

2.2.5 Unlock方法

UnLock方法实现用户内存块的回收, 当用户回收某块内存时, 若该块内存当前正位于物理内存中, 则先要将它交换到外存的映像文件上, 置该内存块的写标志为TRUE, 然后释放其占用的物理内存, 同时修改系统中剩余的物理内存容量;若该块内存当前正位于外存的映像文件上, 则不需对物理内存进行操作;最后修改该块内存的使用频繁度标记。

void Unlock (int serial_num)

{

if (phy_table[serial_num].pAddr != NULL) //in physical memory

{

writeVir (serial_num) ;

phy_table[serial_num].use_count++;

}

}

3 实验与评价

在某型号装备的显控台上进行实验, 该设备配有300 MB内存和8 GB外存, 系统共有500个工作单元, 每个单元的数据吞吐量为8 MB, 最大并发处理能力为32个工作单元。

由上可知500个工作单元共需的数据吞吐量为500×8 MB=4 000 MB, 其内存300 MB是远不能满足需要的。应用本文的虚拟内存实现方法, 设计含有500个表项的页表, 每个页面的大小为8 MB, 则用户可使用的虚拟内存为4 000 MB, 可以满足需要。

当某个工作单元开始工作时, 应用Alloc方法为其申请内存, 500个单元最大可使用4 000 MB的空间。当该工作单元结束工作时, 应用Free方法将申请的内存释放。

当某个工作单元开始数据处理时, 应用Lock方法为其分配内存以供数据吞吐用, 32个单元同时工作时最大数据吞吐量为32×8 MB=256 MB, 显控台上配置的物理内存可以满足需要。当该工作单元在某时刻需要结束数据处理时, 应用Unlock方法回收其占用的物理内存以供其他工作单元使用, 从而满足对全部工作单元的处理需要。

在实际运行过程中, 所有工作单元的数据处理都正常, 未出现数据丢失和错误的情况, 内存交换 (Lock和Unlock调用) 的平均时间是2毫秒, 达到了实用性的要求。

4 结论

本文提出的虚拟内存管理机制, 是一种轻量级的实现方法, 初步达到了实用性的要求, 并具有结构简单、资源消耗小、不依赖MMU硬件的特点, 可以移植在多种嵌入式系统中, 满足嵌入式系统的对内存的需求。

参考文献

[1]钱华明, 张振旅.VxWorks内存管理机制的分析与研究.微计算机信息, 2009;25 (6) :115—117

[2]孙益辉, 陈凯, 白英彩.嵌入式操作系统内存管理机制分析及改进.计算机应用与软件, 2006;23 (3) :99—115

[3]陈洋, 胡向宇, 张坚华.VxWorks下的内存管理.计算机工程, 2007;33 (8) :94—96

[4]刘东栋.VxWorks内存管理机制研究及改进.科学技术与工程, 2007;7 (6) :1218—1220

[5]钱静, 芦东昕, 谢鑫, 等.嵌入式软件虚拟内存管理技术的研究和实现.计算机应用研究, 2009; (2) :115—117

Java虚拟机内存分配探析 第4篇

按照编译原理的观点把内存分配策略划分为3种:静态的、栈式的和堆式的。静态存储分配是指在编译时就能确定每个数据目标在运行时刻的存储空间需求, 因而在编译时就分配固定的内存空间, 这种分配策略要求程序代码中不允许有可变数据结构 (比如可变数组) 的存在。Java语言是面向对象, 并且具有多态性的特点, 因此JVM选用动态内存分配策略:堆和栈。

1 堆和栈

在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配。

当在一段代码块定义一个变量时, Java就在栈中为这个变量分配内存空间, 当超过变量的作用域后, Java会自动释放掉为该变量所分配的内存空间, 该内存空间可以立即被另作他用。

堆内存用来存放由new创建的对象和数组。在堆中分配的内存, 由Java虚拟机的自动垃圾回收器来管理。在堆中产生了一个数组或对象后, 还可以在栈中定义一个特殊的变量, 让栈中这个变量的取值等于数组或对象在堆内存中的首地址, 栈中的这个变量就成了数组或对象的引用变量。引用变量就相当于是为数组或对象起的一个名称, 以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象。

Java是自动管理栈和堆, 程序员不能直接地设置栈或堆。

Java的堆是一个运行时数据存储区, 类的对象从中分配空间。对象通过new、newarray等方式建立, 不需要程序代码来显式地释放。堆是由垃圾回收来负责的, 堆的优势是可以动态地分配内存大小;缺点是, 存取速度较慢。

栈的优点是, 存取速度比堆要快, 仅次于寄存器。栈数据可以共享;缺点是, 存在栈中的数据大小与生存期必须是确定的, 缺乏灵活性。栈中主要存放一些基本类型的变量 (int, short, long, byte, float, double, boolean, char) 和对象句柄。

栈有一个重要特点, 就是存在栈中的数据可以共享。假设定义:

编译器先处理int a=3;首先会在栈中创建一个变量为a的引用, 然后查找栈中是否有3这个值, 如果没找到, 就将3存放进来, 然后将a指向3。接着处理int b=3;在创建完b的引用变量后, 因为在栈中已经有3这个值, 便将b直接指向3。这样, 就出现了a与b同时均指向3的情况。这时, 如果再令a=4;那么编译器会重新搜索栈中是否有4值, 如果没有, 则将4存放进来, 并令a指向4;如果已经有了, 则直接将a指向这个地址。因此a值的改变不会影响到b的值。要注意这种数据的共享与两个对象的引用同时指向一个对象的这种共享是不同的, 因为这种情况a的修改并不会影响到b, 它是由编译器完成的, 它有利于节省空间。而一个对象引用变量修改了这个对象的内部状态, 会影响到另一个对象引用变量。

2 String类对象举例说明

String是一个特殊的包装类数据。可以用:

两种的形式来创建, 第一种是用new () 来新建对象的, 它会在存放于堆中。每调用一次就会创建一个新的对象。

而第二种是先在栈中创建一个对String类的对象引用变量str, 然后查找栈中有没有存放“abc”, 如果没有, 则将“abc”存放进栈, 并令str指向“abc”, 如果已经有“abc”, 则直接令str指向“abc”。

比较类里面的数值是否相等时, 用equals () 方法;当测试两个包装类的引用是否指向同一个对象时, 用==, 下面用例子说明上面的理论。

因此用第二种方式创建多个“abc”字符串, 在内存中其实只存在一个对象而已。这种写法有利于节省内存空间。同时它可以在一定程度上提高程序的运行速度, 因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于String str=new String (“abc”) ;的代码, 则一概在堆中创建新对象, 而不管其字符串值是否相等, 是否有必要创建新对象, 从而加重了程序的负担。

尤其需要注意的是:在使用诸如String str=“abc”;的格式定义类时, 总是想当然地认为, 创建了String类的对象str。事实是对象可能并没有被创建, 而可能只是指向一个先前已经创建的对象。只有通过new () 方法才是每次都创建一个新的对象。

3 结束语

每一个Java应用都唯一对应一个JVM实例, 每一个实例唯一对应一个堆。应用程序在运行中所创建的所有类实例或数组都放在这个堆中, 并由应用所有的线程共享。Java中分配堆内存是自动初始化的。Java中所有对象的存储空间都是在堆中分配的, 但是这个对象的引用却是在栈中分配, 也就是说在建立一个对象时从两个地方都分配内存, 在堆中分配的内存实际建立这个对象, 而在堆栈中分配的内存只是一个指向这个堆对象的指针 (引用) 而已。

摘要:Java把内存划分为堆和栈。介绍了堆和栈的区别, 并以String类对象为例说明它们在内存分配中的不同, 及对程序编写的影响。

关键词:JVM,堆,栈,string类

参考文献

[1]任哲.Java技术应用基础——对象.模式.虚拟机[M].北京:机械工业出版社, 2009.

[2]Java虚拟机内存分配与回收机制[EB/OL].http://www.yqdown.com/chengxukaifa/Java/11580.htm.

[3][美]BILL VENNERS.深入Java虚拟机[M].北京:机械工业出版社, 2003.

虚拟内存管理 第5篇

驻留段主要用作磁盘数据的缓存。虚拟段主要用作内存池以支持进程及相关的会话 (session) 和线索。当客户与服务器利用共享内存进行通信时, 消息段将用作两者之间的消息缓冲区。

共享内存虚拟段主要用做内存池以支持会话和线索。根据用途的不同, 内存池可以进一步分为:会话池、多线索池、字典池、存储过程池、排序池、大缓冲区池以及全局池。内存池的分配和释放是动态进行的。内存池的大小为8k。如果已有虚拟段耗尽, 动态服务器可以根据onconfig参数再次动态申请。虚拟内存段的初始大小由参数SHMVIRTSIZE决定, 其增量由参数SHMADD决定, 但整个共享内存段的大小不能超过SHMTOTAL。

所有的会话都有一个或多个内存池。当数据库服务器需要内存时, 它会先查看指定的池。如果池中可用的内存不足以满足请求, 则数据库服务器将从系统池添加内存。如果数据库服务器不能在系统池中找到足够的内存, 则它会动态地给虚拟部分分配更多的段。

数据库服务器从通过已链接的列表跟踪可用空间的池为它的每个子系统 (会话池、堆栈、堆、控制块、系统目录、SPL、例程高速缓存、SQL语句高速缓存、排序池和消息缓冲区) 分配虚拟共享内存。当数据库服务器分配内存的部分时, 它将首先在池的可用列表中搜索大小足够的段。如果服务器未找到段, 则会将新的块从虚拟部分带入池中。如果释放了内存, 则它将作为可用段返回到池中并且留在那里直到池遭到破坏。例如, 当数据库服务器启动了客户机应用程序会话时, 它将为会话池分配内存。当会庆终止时, 数据库服务器会将已分配的内存作为可用段返回。

用天估计共享内存虚拟部分初始大小的基本算法如下:

s hm v1rts 1ze=固定开销+共享结构+ (m ncs*专用结构) +其它缓冲区

用上述的公式估计SHMVIRTSIZE:

使用以下公式估计固定开销:

固定开销=全局池+引导后的线程池

使用onstat-gmem命令获取分配会话的池大小。从totalsize值中减去freesize字段中的值即可得到分配给每个会话的字节数。引导后的线程池变量部分地取决于虚拟处理器数。

使用以下公式估计共享结构:

共享结构=AIO向量+排序内存+

数据库空间备份缓冲区+

数据字典高速缓存大小+

用户定义的例程高速缓存大小+

直方图池+

STMTCACHESIZE (SQL语句高速缓存) +

其它池 (查看分配给不同池的内存数量, 可使用onstat-g mem命令)

要估计公式的下一部分, 请执行以下步骤:

用以下公式估计mncs (并发会话的最大数) :

m ncs=轮询线程数*每个轮询线程连接数。

轮询线程数的值就是您在NETTYPE配置参数的第二个字段中指定的值。每个轮询线程连接数的值就是您在NETTYPE配置参数的第三个字段中指定的值。

在高峰处理期间执行onsta-u命令时, 还会得到并会话最大数的估计。Onstat-u输出的最后一行包含最大的并发用户线程数。

用以下公式估计专用结构:

专用结构=堆栈+堆+会话控制块结构

堆栈:通常是32千字节, 但是取决于用户定义的例程中的递归可以用onstat-gsts选项来获取每个线程的堆栈大小。

堆:约为15千字节。当使用onstat-g stm选项时, 您可以获取QL语句的堆大小。

会话控制块结构:是每个会话使用的内存量。Onstat-gses选项在为每个会话标识列出的totalmemory列中显示内存量 (以字节为单位) 。

将步骤3a和3b的结果相乘以得到公式的以下部分:

m ncs*专用结构

估计其它缓冲区以说明分配给各种功能 (如智能大对象 (大约每个用户180KB) 的轻量级I/O操作功能) 的专用缓冲区。

将步骤1到4的结果相加, 可获取SHMVIRTSTZE的估计值。提示:当数据库服务器正以稳定的工作负载运行时, 可以使用onstat-g s e g来获取虚拟部分实际大小的精确值。然后, 可以使用此命令报告的共享内存值重新配置SHMVIRTSIZE。SHMVIRTSIZE的经验估算值:设置为32000+可能用户数*800。我们建议为初始虚拟段一次性分配最大可能的内存而避免由系统追加分配。利用onstat-gseg命令可以监控虚拟段的使用情况, 请注意那些被标记为V类的段。如果发现系统为虚拟段动态增加了空间, 则应扩大参数SHMVIRTSIZE。利用onm ode-F命令可以释放空闲的共享内存段, 但这可能影响系统性能。

SHMADD指定动态添加到共享内存的虚拟部分的段的大小。在大段中添加内存更有效, 但如果不使用已添加的内存, 则造成浪费。同样, 操作系统可能要求您在小数几个大段中添加内存, 而不是在许多小段中添加。

Inform ix共享内存虚拟段对设置SHMADD的初始值的建议:物理内存量小于256兆字节时, 建议的SHMADD值在8192。物理内存量在256兆字节和512兆字节之间, 建议的SHMADD值在16, 384。物理内存量大于512兆字节, 建议的SHMADD值在32, 768。

摘要:Informix共享内存虚拟段主要用做内存池以支持会话和线索。所有的会话都有一个或多个内存池。当数据库服务器需要内存时, 它会先查看指定的池。如果池中可用的内存不足以满足请求, 则数据库服务器将从系统池添加内存。如果数据库服务器不能在系统池中找到足够的内存, 则它会动态地给虚拟部分分配更多的段。文章就Informix共享内存虚拟段分配进行探讨和分析。

虚拟内存管理 第6篇

对于手机来说,内存资源是非常有限的,良好的内存分配管理机制是至关重要的.如果内存没有得到良好的分配及捕获清理,会导致内存溢出、数据丢失,系统崩溃等严重后果发生。本文主要是讨论在Symbian开发过程中,内存管理机制的实现过程和一些注意事项。

Symbian操作系统的一个显著特点是针对内存空间和资源均受限的设备设计的,所以高效的使用这些有限的资源成为重点。作为手持设备的开发者,必须要注意以下几个问题:

(1)有效率的编程,减少对RAM的不必要的使用;(2)资源使用完毕后尽快释放;(3)需要对资源不足情况进行处理,这要求在每次内存分配的时候都进行;(4)如果在程序运行过程中出现内存不足的情况,应该使程序回到一个可以接收的稳定状态,并且清除在此过程中分配的资源。上述考虑对于一个成功的手持设备开发者是非常重要的。一个手持设备的操作系统如果不能够在以上问题上对开发者提供良好的支持,则无疑它是失效的。

Symbian OS主要提供了三种机制来管理内存的。它们分别为:TRAP和Leave、清除栈、两阶段构造函数。

1 TRAP和Leave

在开发Symbian OS的时候,异常处理还不是C++标准的一部分,所以Symbian没有采用C++后来引进的抛出/捕获异常处理方法,而是使用了自己的异常退出/捕获方法作为简单高效的轻量级的异常处理机制。Symbian OS使用User::Leave()、User::LeaveI-fError()等系统函数来抛出异常,提供TRAP和TRAPD两个宏来捕获异常,这与标准C++中的throw和catch类似。

1.1 抛出异常

Symbian OS中的函数并不返回出错代码,而是一出现资源不足错误时就抛出异常。Symbian OS使用User::Leave()、User:LeaveIfError()等系统函数来抛出异常。

(1)User::Leave():该函数的原型为static IMORT_C void Leave(TInt aReason);该函数将导致程序回溯到最近的异常捕获模块,并将异常代码aReason传递给异常捕获模块。所有可以异常退出的函数都以字母'L'结尾。

(2)User::LeaveIfError():该函数的原型为static IMORT_C void LeaveIfError(TInt aReason);如果aReason为负数,该函数将导致程序回溯到最近的异常捕获模块,并将异常代码a Reason传递给异常捕获模块;如果aReason为非负数,该函数仅返回aReason。

1.2 异常捕获

在Symbian OS中,出现的错误和异常要能够及时捕获并处理。Symbian OS提供了TRAP和TRAPD这两个宏来捕获异常。TRAP与TRAPD功能是一样的,它们使用的区别在于,TRAPD声明的变量包含了错误代码,TRAP只不过少了变量声明,因此需要自己在程序声明一个变量来保存错误值.以下是TRAP与TRAPD的比较:

需要注意的是:大量的TRAP会增加可执行文件的大小和影响程序的执行速度。因为每次进入和退出TRAP宏都会导致内核执行调用,在运行时还需要分配一个结构体来保存异常退出发生时栈的内容,这样当异常退出发生并沿调用栈传播时,才有可能恢复现场的状态。因此,尽量少使用或者替代使用TRAP,会节省很多资源。不要连续使用多个TRAP,而是利用方法Leave,从而将错误交给调用者处理。

2 清除栈

正常申请堆空间后,堆和栈的空间分配如图1所示。如果一个函数产生异常,程序会回溯到异常捕获模块。这意味着栈空间中的自动变量都被删除了。如果这些栈空间的自动变量包含有指针,而自动变量指针通常会指向一个堆空间,堆空间的对象永远不会被删除,这时就发生了比较严重的问题:内存泄露。如图2所示。

清除栈就是用来处理这个问题的。它用来保存那些在发生异常后需要释放资源的对象指针。当Leave发生时,通过保存在清除栈中的指针,来删除它们所指向的对象。如下代码所示:

当我们申请完Cclanger并得到它的指针clanger后,立即将指向Cclanger的自动变量指针Push到清除栈中。然后调用InitializeL()或DoSomethingElseL();如果它们没有Leave,则CleanupStack::PopAndDestroy(clanger)被执行,将指针clanger从清除栈中弹出并将对象Cclanger删除;如果InitializeL()或DoSomethingElseL()发生了Leave,Trap harness将调用PopAndDestroy()来清空清除栈上所有从调用Trap时压进去的变量,并释放相应的堆空间。如图3所示,加入清除栈后栈和空间的分配图。

当发生异常退出后,指针clanger和自动变量被栈空间管理进程删除,此时CClanger对象内存空间由清除栈中的clanger*来释放,避免了内存泄露。

需要注意的是,在使用清除栈时,有压入就要有弹出,保证清除栈的压入和弹出操作在逻辑上是匹配的。只能把自动变量压入清除栈,而非成员变量,因为Symbian C++实际上有两种清除机制,一种是清除栈,另一种是析构函数。这两种机制各有分工,并行不悖。在发生异常后,成员变量的清除工作由析构函数来处理。如果将成员变量压入清除栈,则该变量会被清除两次。

3 两阶段构造函数

当在堆上进行实例化过程中,首先,程序为类的实例化申请内存,执行成功;紧接着,程序执行该类的构造函数,又要申请内存,但系统内存耗尽,执行失败。在这种情况下,若不触发异常退出,则构造函数不能将初始化失败的信息传递出来,我们接收到的对象指针指向的是一个构造不完全的对象。这将对之后的程序运行产生不可预知的影响。如果触发异常退出,则程序回溯到最近的异常处理模块,进行相关的异常处理。可是程序已经为该类的实例化申请了内存,但其指针却没有及时加入到清除栈,此时就会造成内存泄露。

上述问题的主要症结就在于类的构造函数可能发生异常。于是将此问题一分为二对待,得到两阶段构造函数,即将一个对象的构造分为两个阶段:第一个阶段是不能异常退出的构造函数;将可能产生异常的初始化操作从构造函数中抽出,放在第二个阶段中,用函数ConstructL()来实现。

对象的构造过程包括了如下的代码:

CClassName*self=new(ELeave)CClassName();//第一阶段构造

CleanupStack::PushL(self);

self->ConstructL();//第二阶段构造

CleanupStack::Pop(self);

从以上代码我们可以看出,每实例化一个对象就要写上述代码有些过于麻烦,为了简化过程,Symbian OS又引入了NewL()NewLC()两个静态函数,典型的NewL()和NewLC()实现如下:

CClassName*CClassName::NewLC()

{

CClassName*self=new(ELeave)CClassName();

CleanupStack::PushL(self);

self->ConstructL();

return(self);

}

CClassName*CClassName::NewL()

{

CClassName*self=CClassName::NewLC();

CleanupStack::Pop(self);

return(self);

}

可以看出,NewL()构造的对象,没有被压入清除栈,NewLC()构造的对象被压入清除栈中。我们可以根据具体情况选择使用。有了NewL()和NewLC()后,再创建CclassName时,就可以用代码CClassName*iClassName=CClassName::NewLC();或者

CClassName*ClassName=CClassName::NewLC();了,从而程序得到大大简化。

4 结束语

Symbian操作系统是专门针对内存空间及资源均受限的设备设计的,它在内存空间管理方面有着不同于其他平台的特有机制,内存管理更加高效,有效的避免了内存泄露造成的严重后果。但其高效的内存管理是以牺牲兼容性为代价的,相信Symbian OS在今后的发展中会不断地完善不足之处,其兼容性也会越来越高。

参考文献

[1]马建,陈健.智能手机操作系统编程-Symbian及60系列[M].北京:科学出版社,2005.

[2]董佩嘉.Symbian异常处理及预防研究[J].电脑知识与技术,2007(2).

[3]郑朝霞,赵岚.基于Symbian S60的内存管理[J].软件导刊,2008(8).

JAVA内存管理模式研究 第7篇

JAVA自上个世纪90年代初期诞生以来, 发展到今天已经被业界广泛的认可, 其为编程 (尤其是网络编程) 方面所带来的巨大变革, 是其他语言所不可比拟的。它以自身的纯粹的面向对象, 分布式, 安全性, 健壮性, 与平台无关性等特点正在被全世界的程序员所推崇。但伴随而来的也有一些质疑, 比如JAVA在编制桌面程序, 以及在程序的执行效率方面确实还有差强人意的地方, 其原因何在?本文试就上述问题, 从内存的角度分析JAVA的内部机制, 以使读者更深入地了解和掌握JAVA语言。

二、Java的内存分配策略

JAVA程序在运行时, 不同平台上的JVM都会提供如下图所示的运行时数据区组件:

JAVA是如何来管理分配内存的呢?Java的内存分配主要有三种:一是方法区, 存放静态变量和方法信息。该区域在程序编译时就分配好了空间大小, 为了避免导致编译程序无法计算准确的存储空间需求, 这种分配方式要求程序中不能有不确定的数据结构, 更不允许有嵌套, 甚至于递归等结构的出现;二是栈区, 即各种原始数据类型的局部变量都是在栈上创建出来的, 并且当程序退出该变量的作用范围的时候这个变量的内存就会被自动释放。和方法区的分配相反, 在栈区, 程序在编译时对数据的状态是未知的, 这要到运行时才能知道。三是堆区, 该区域负责分配在编译时和运行时都不能确定存储需求的数据结构的内存, 如对象 (包括数组) 都是在堆中创建的。程序在运行时通过new关键字来创建对象, 对象创建时会在堆中为其分配内存, 当对象不再被用到时, 会被GC垃圾回收机制自动回收, GC这时就必须监控对每一个对象的运行状态, 如对象的申请、引用、被引用、赋值等, GC都需要进行监控, 其目的是简化了程序员的工作。但却加重了JVM的工作。这也就是为什么Java程序运行速度较慢的原因。

除了这三个区域外, 还有寄存器和常量池。其中的寄存器, 由于处在处理器内部, 所以运行起来速度肯定是最快的, 但是由于其容量非常小, 且是直接受处理器控制而非人为控制, 所以我们无法掌控。常量池是用来存放确定性的符号引用的, 如类和接口的全限定名, 字段和方法的描述符等。

三、JVM中堆和栈的区别与比较

在JAVA中, 栈与堆在内存中都被Java用来存放数据。Java自动管理栈和堆, 这一点与C++不同。单纯从功能作用来说, 堆主要用来存放对象, 栈中一般用来存放一些基本类型的变量和对象句柄。

(一) 栈 (stack) , 存在于RAM中。

栈对内存的操作方式是, 从处理器那里得到指令后, 通过内部的“栈指针”来具体执行, 指针向下移动就是分配一个新内存给变量;如指针向上移动, 则代表释放该部分内存。这种分配方式方便快捷, 效率仅次于寄存器。由于栈在运行时要确定所有数据的大小以及它们的“生命周期”, 以便操作栈指针进行相应的移动, 这就对程序的灵活度造成了一定的影响。所以, JAVA只是把所有的局部变量, 形式参数以及对象的句柄变量等分配到栈中, 因为这些内容在运行时都可以是确定的。另外, 方法调用也是在栈内进行的, 调用方法的时候, 栈为该方法的参数及局部变量分配空间, 当方法调用结束以后, 这部分空间会被自动释放出来。

栈的特点决定了它的优势是速度快, 存取效率仅次于寄存器。但存于其中的数据大小与生存期在运行时需要被确定, 使得灵活性不够, 则是其缺点所在。

(二) 堆 (heap) , JAVA中对象和数组的生成是在堆中完成的。

以往一般的编程语言不会选择在堆中分配内存资源, 因为效率较低。但是JAVA中由于引入了自动垃圾回收机制 (GC) , 使得堆中分配内存的速度明显加快, 甚至于能够接近于其他语言在栈中分配内存的速度。其内存分配的具体过程是这样的, 在JVM中保存有一个堆指针, 指向于堆中未被分配的内存, 当生成一个新的对象时, 该指针只需改变一下内部的记录并做一个简单的移动操作, 指向下一片未分配空间, 所以效率会跟栈不相上下。但如果单纯这样操作而不采取一些其他辅助措施, 在生成新对象时势必会造成频繁的内存分页并产生大量的内存碎片, 使得内存的消耗很大, 进而影响到系统性能。出于这方面考虑, JAVA中引入了GC的内存管理机制, GC在检测到内存资源紧张时被自动调用运行, 在程序中从根节点开始去找所有的对象引用, 当发现某个对象不再被用到时, GC会自动收回该对象所占用的堆内存空间并加入到空闲列表里, 留待下一次分配内存时使用, 在这一过程中还会对堆中的对象内存进行优化, 使它们紧密排列在一起, 这样一来就使得一些零散的内存碎片重新组合在了一起, 从而避免下一次分配内存空间时分页失误的现象发生。显然, GC机制的引入, 减轻了程序开发人员的工作任务, 降低了程序代码的复杂度, 并在一定程度上控制了系统中潜在的危险因素。但GC在内存回收方面不是万能的, 它只负责监控由new生成的对象, 而且运行过程中消耗时间, 所以, 在编程中我们要科学地安排代码, 尽量避免有过多内存垃圾产生。比如, 当对象不再被使用时应显式将其赋值为null, 以便节省GC检测时所消耗的时间。

堆的优势是在运行时动态地分配内存, 而内存的管理交由GC自动处理, 从而实现了高速、自由、灵活的堆空间分配模式。

(三) 关于二者存储效率方面的比较。

我们用如下示例来说明:

我们把Test_1类中这两种创建对象的形式做下比较, 第一种用new的形式, 系统读到new关键字的时候就在堆内存中生成一个新的对象保存起来。每读到一次new就生成一个新对象。第二种形式则是先在栈内存中生成一个对String对象的引用的一个变量str2, 然后查找栈中有没有存放"qjw"字符串对象的变量, 若没找到的话, 则将str2指向”qjw”字符串对象, 如果已经有指向于”qjw”对象的变量存在, 则直接令str2中的值等于该变量中保存的地址值。

在程序中我们来验证一下:

上面的结果验证了str1和str2是指向于同一个对象的。而实际上在内存中只有一个对象而已, 这种做法有利于节省内存空间, 同时它可以在一定程序上提高程序的运行速度, 因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于String str=new String (“qjw”) ;的代码, 则一概在堆中创建新对象, 每读到一次new, 就在堆内存生成一个新对象。而不管其字符串值是否相等, 是否有必要创建新对象, 从而加重了程序的负担。

四、结语

本文从内存的角度详细分析了堆内存和栈内存的特点及区别, 通过二者的比较, 目的在于使读者能对JAVA在内存管理方面有更深入一层的了解, 从而在编程中对内存的使用方面更加得心应手, 对于学习理解乃至于编写JAVA程序都非常重要。通过比较我们知道, 虽然栈的优势是存取速度比堆快, 但其灵活性方面还显得很欠缺。而堆的优势却是可以在运行时动态地分配内存大小, 在内存管理方面更加灵活, 因为有Java内置的垃圾收集器帮助其对内存进行管理, 但这也要付出一定的开销, 因为GC随时在监测内存中对象的存续状况, 以便发现不用的对象及时对其进行回收, 所以在某种程度上就付出了效率的代价, 但总体上不失为JAVA在内存管理方面的优秀表现。

摘要:由于JAVA的纯面向对象的特性, 编程中会频繁地进行对象的操作, 深入理解对象及变量方法等在内存中的分配过程有助于设计合理高效的程序, 对于Java虚拟机优化垃圾收集及程序的能耗优化有着重要的指导意义。本文通过对Java虚拟机内存区域的分析, 对内存的分配问题进行了详细地阐述。

关键词:Java, 堆内存,栈内存,静态域,常量池,内存分配

参考文献

[1].章婧, 卢凯.JAVA程序内存行为研究[J].小型微型计算机系统, 2011

[2].黄山, 杨全胜, 杜中军.Java内存管理和内存泄漏的研究[J].中国民航飞行学院学报, 2009

[3].黄珍, 刘涛.JAVA中对栈与堆的一点思考[J].九江职业技术学院, 2009

本文来自 99学术网(www.99xueshu.com),转载请保留网址和出处

【虚拟内存管理】相关文章:

内存管理07-10

内存管理专题要点07-24

内存保护08-07

内存分析08-17

内存容量08-19

动态内存分配05-28

基于内存的文件系统07-01

内存优化工具的骗局05-29

塑料内存的发展现状07-20

挑战Windows极限:物理内存07-10

上一篇:滑塌处理下一篇:网络架设