讨论了Java关键技术组成,深入介绍了Java虚拟机的体系结构,分析了虚拟机中内存管理的垃圾回收机制。同时,对现有的一些流行垃圾回收算法进行了深入分析和对比以及对某些算法的改进。Java虚拟机中存在多种垃圾回收机制,通过对Java虚拟机中内存管理机制的深入分析,可以使开发者针对不同的内存分配及回收策略选择合适的虚拟机。
目录
1 JVM 体系结构
1.1 体系结构
1.2 内存分配
2 垃圾回收
2.1 回收算法
2.1.1 引用计数查找算法
2.1.2 跟踪查找算法
2.1.3 压缩算法
2.1.4 火车算法
3 结 语
4 结 语
1 JVM 体系结构
J ava体系结构包含了4个独立但相关的技术:Java程序设计语言;Javaclass 文件格式;Java应用编程 接口(API);Java虚拟机。
Java面向网络的核心就是Java虚拟机,他支持Java面向网络体系结构3大支柱的所有方面:平台无关性、安全性和网络移动性。
1.1 体系结构
每个Java虚拟机都有一个类装载器子系统,他根据给定的全限定名来装入类型(类或接口)。同样,每个Java虚拟机都有一个执行引擎,他负责执行那些包含在被装载类的方法中的指令。当Java虚拟机运行一个程序时,他需要内存来存储许多东西,例如字节码,从已装载的class文件中得到的其他信息,程序创建的对象,传递给方法的参数,返回值,局部变量,以及运行的中间结果等等。Java虚拟机把这些东西都组织到几个“运行时数据区”中,以便管理。
尽管这些“运行时数据区”都会以某种形式存在于每一个Java虚拟机实现中,但是规范对他们的描述却是相当简单的。这些运行时数据区结构上的细节,大多数都由具体实现的设计者决定。不同的虚拟机实现可能具有不同的内存限制,有些实现可能有大量的内存可用,有些可能只有很少。有些实现可以利用虚拟内存,有些则不能。规范本身对“运行时数据区”只有抽象的描述,这就使Java虚拟机可以很容易地在各种计算机智能设备上实现。
1.2 内存分配
即使是最简单的计算机,其内存需求量也在飞快增长,像一切有限的资源一样,内存也需要小心地保存和回收。高级程序设计语言必须依赖编译器分配目标计算机的资源,其中重要的一点就是分配内存。内存可以通过3种方式分配:静态分配是最简单的内存分配策略。程序中的所有名字在编译时绑定在某个存储位置上:这些绑定不会在运行时改变;栈分配,块结构语言通过在栈上分配内存,克服了静态分配的一些限制。每次过程调用时,一个活动记录或是帧被压入系统栈,并在返回时弹出;堆分配与栈所遵循的后进先出的规律不同,堆中的数据结构能够以任意次序分配与释放。因而活动记录和动态数据结构可能比创建他们的过程更长寿。
在Java虚拟机的堆里存放着正在运行的Java程序所创建的所有对象。使用new,newarray,anewarray和 mul-tianewarray指令来创建对象,但是没有专门的代码来释放他们,垃圾收集的工作就是自动释放不再被程序所使用的对象的过程。对于不同的虚拟机,垃圾回收机制就成为了判断虚拟机性能的一项指标。
2 垃圾回收
Java虚拟机规范中没有要求任何特定的垃圾收集技术,但是显然在发明可以无限度使用的内存前,垃圾收集都是任何虚拟机所必须的。垃圾收集更准确的说法是“内存回收”。当一个对象不再被程序所引用时,他所使用的堆空间可以被回收,以便被后续的新对象所使用。垃圾收集器必须能断定哪些对象是不再被引用的,并且能够把他们所占据的堆空间释放出来。在释放不再被引用的对象的过程中,垃圾收集器只需主动运行将要被释放的对象的终结方法(finalizer)。
2.1 回收算法
任何垃圾收集算法都必须做两件事情。首先,他必须检测出垃圾对象。其次,他必须回收垃圾对象所使用的堆空间并还给程序。在Java虚拟机实现中,有些垃圾收集器可以区别真正的对象引用和看上去像合法对象引用的基本类型(比如一个int)之间的差别。例如,一个int整数,如果被解释是一个本地指针,可能指向堆中的一个对象。可是某些垃圾收集器仍然选择不区分真正的对象引用和“伪装品”,这种垃圾收集器被称为保守的(conserva-tive),因为他们可能不能总是释放每一个不再被引用的对象。除了释放不再被引用的对象,垃圾收集器还要处理堆碎块。垃圾收集把用户从释放占用内存的重担中解救出来。知道何时明确地释放内存是非常需要技巧的。
2.1.1 引用计数查找算法
该算法是为每一对象分配一引用计数器,当对象创建时,计数器置为1,以后当有其他的对象或根引用该对象时,计数器加1,当引用该对象的根或其他对象脱离定义范围或被赋予其他值时,计数器减1,当计数器为零时,回收该对象,并将被该对象引用的其他对象的计数器减1,该过程可导致一系列的无用单元回收。该算法的优点是运行时间短,回收效率高。缺点是无法检测出循环引用。循环引用是指两个或更多的对象之间直接或间接相互引用。在循环引用的对象中,计数器不可能为零,因此即便是无用单元也不可能被回收。因此,JVM 中一般不采用该算法。
2.1.2 跟踪查找算法
该算法首先确定一组树根,根为Java栈的局部变量及引用对象的常数池,根确定后以根为起点,对对象引用图进行搜索(深度优先或广度优先),并标记搜索过程中遇到的对象,搜索完毕后,没有标记的对象即为无用对象。Java对象有些有finalizer()方法,有些没有。含有该方法的垃圾对象释放前应执行finalizero方法释放系统资源。若某一不含finalizero方法的垃圾对象被另一含finalizer()方法的垃圾对象引用则应先释放后者。
2.1.3 压缩算法
回收对象时,不可避免地会产生堆碎片,为有效地利用堆,必须进行碎片合并。一般采用如下策略:
1、压缩策略 将所有对象依次水平移动到对象堆的一端,则对象堆的另一端为连续的空白区,移动的同时其他变量对被移动对象的引用修改为新的对象位置。为便于修改引用,变量引用对象时没必要指向对象,只需引用对象柄(objecthandle),而对象柄直接引用对象。
2、复制策略 将所有的有用对象拷贝到一个新的对象堆,在新堆中对象顺序排放,从而消除碎片。旧堆则为空白区。该算法占用内存较大,但可一边查找一边复制有用对象,效率较高。对象复制到新堆后,在旧堆中对象原位置处保存一位置指针,为以后查找遇到该对象时指明位置。
2.1.4 火车算法
垃圾收集算法和明确释放对象比起来有一个潜在的缺点,即垃圾收集算法中程序员对安排CPU时间迸行内存回收缺乏控制。要精确地预测出何时(甚至是否)进行垃圾收集、收集需要多长时间,基本上都是不可能的。因为垃圾收集一般都会停止整个程序的运行来查找和收集垃圾对象,他们可能在程序执行的任意时刻暂停,并且暂停的时间也无法确定。火车算法最早是由理查德·哈德森(RichardhHudson)和埃里特·莫斯(EliotMoss)提出的,目前正用于Sun公司的 Hotspot虚拟机,该算法详细说明了按代收集的垃圾收集器的成熟对象空间的组织。火车算法的目的是为了在成熟对象空间提供限定时间的渐进收集。
火车算法把成熟对象空间划分为固定长度的内存块、算法每次在一个块中单独执行。“火车算法”这个名字来源于算法组织这些块的方式。每一个块属于一个集合。在一个集合内的块排了序,这些集合本身也排了序。在哈德森和莫斯的原始论文中,为了更好地解释算法,把块叫做“车厢”,把集合叫做“火车”。使用这个比喻,成熟对象空间扮演火车站的角色,同一个集合中的块被排序,就如同一列火车中的车厢是有顺序的。成熟对象空间中的集合被排序,很像在火车站中火车按照轨道l、轨道2、轨道3等排列。每一次火车算法被执行的时候,他要么收集最小数字火车中的最小数字车厢,要么收集整列最小数字火车。算法首先检查指向最小数字火车中任何车厢的引用,如果不存在任何来自最小数字火车以外的引用指向他内部包含的对象,那么整列最小数字火车包含的都是垃圾,可以被抛弃。这第一步使得火车算法可以一次收集大型的、无法在一个块中容纳的循环数据结构。在下面将要描述的火车算法步骤中,这种大型的循环数据结构,保证可以在同一火车中被销毁。
3 结 语
垃圾回收是Java语言的一大特色,因为他使得Java语言成为一种简单而安全的语言。但是垃圾回收并不是Java的创举,早在10多年前,在 Lisp语言和函数式语言等领域,垃圾回收己经逐渐成熟,但是今天垃圾收集已经成为许多现代程序设计语言内存管理系统的重要部分。本文首先谈了垃级收集对内存分配与管理中起到的作用,然后综述了比较流行的使用于Java虚拟机的实现中的几种垃圾回收算法。然而对于不同的虚拟机所采取的垃圾回收算法也有所不同,这也有待进一步研究。
4 结 语
本文提出了一种基于SOPC技术的线阵CCD数据采集和存储系统的实现方法。使用高速 A/D转换器、2个小容量的FIFO和SOPCBuilder开发组件中的DMA模块完成了数据采集和存储功能,实现方法简单,能够进行数据的连续采集存储。该方法占用CPU时间短,使CPU有更多时间进行后续的数据处理工作。该系统在 Altera公司的CycloneFPGA实现,具有开发周期短、集成度高等特点。软硬件均采用编程实现,设计灵活,容易修改,在实际应用中收到很好的效果。