当前位置:文档之家› java垃圾收集机制

java垃圾收集机制


二、垃圾收集的基本原理:
2.1 JVM 中内存的划分
垃圾收集器对 Java 程序员来说,基本上是透明的,但是只有了解 GC 的工作 原理、如何优化 GC 的性能、如何与 GC 进行有限的交互,才能提高整个应用程序 的性能、全面提升内存的管理效率,为了说明其工作方式,我们首先看看内存中 几种常用的存放数据的地方: (1) 堆栈(Stack) : 位 于常规 RAM(随机访问存储器)区域,但可通过它的 “堆栈指针”获得处理器的直接支持。堆栈指针若向下移,会创建新的内存;若 向上移,则会释放那些内存。这是一种特别快、特别有效的数据保存方式,仅次 于 CPU 的寄存器。创建程序时,Java 编译器必须准确地知道堆栈内保存的所有
2、基于 Tracing 算法、 Compacting 算法的 “标记和清除 (make-and- sweep)”方式:对于常规性的应用,标记和清除显得非常慢,但一旦知道自己 不产生垃圾,或者只产生很少的垃圾,它的速度就会非常快。标记和清除(make -and-sweep)采用的逻辑:基于 Tracing 算法从堆栈和静态存储区域开始,并 跟踪所有引用,寻找活动对象。然而,每次发现一个活动对象的时候,就会设置 一个标记(一个位或多个位) ,为那个对象作上“记号”。但此时尚不收集那个 对象。只有在标记过程结束,清除过程才正式开始。在清除过程中,不复存活的 对象会被释放然而,不会进行任何形式的复制(这时的堆中被使用的空间呈现不 连续的状态) 。所以假若收集器决定使这个断续的内存堆密集(compact),它将 使用 Compacting 算法重新整理他所找到的对象:垃圾收集器将所有的对象移置 堆的一端。堆的另一端就变成了一个相邻的空闲内存区。收集器会对它移动的所 有对象的所有引用进行更新,这样这些引用能够在新的位置识别同样的对象。为 了 简 化 对 象 引 用 的 更 新 , compacting 算法 增 加 了 间 接 的 一 层 ( level of indirection) 。间接层通过句柄(handle)和句柄表实现。在这种情况下,对象 引用总是指向句柄表中同样的句柄入口。反过来,句柄入口包含了句柄的实际引 用。当 compacting 标记和清除垃圾收集器移动对象时,收集器在句柄表中只修 改实际的对象引用对句柄入口的所有引用没有受到影响。 虽然使用句柄表简化了
4
Java 垃圾收集机器-Garbage Collector
发现这个问题,就得通过后续过程才能找到。 但是在有的情况下将使得“复制式垃圾收集器”显得效率极为低下,比如随 着程序进入了稳定状态之后,它几乎不产生或产生很少的垃圾。尽管如此,这时 的一个“复制式垃圾收集器”仍会将所有内存从一处复制到另一处,这显得非常 浪费。更糟的是:程序中的对象不仅不死亡,而且一个个都很庞大,对两个大的 内存堆管理也将成为不必要的消耗。
2
Java 垃圾收集机器-Garbage Collector
数据的“长度”以及“存在时间”。这是由于它必须生成相应的代码,以便向上 和向下移动指针。这一限制无疑影响了程序的灵活性,所以尽管有些 Java 数据 要保存在堆栈里——特别是对象的引用(也可称为对象的引用变量) ,但 Java 中的对象不会放在其中。 (2) 堆(Heap)。一种常规用途的内存池(也在 RAM 区域) ,其中保存了 Java 对象。和堆栈不同,“内存堆”或“堆”(Heap)最吸引人的地方在于编译器不 必知道要从堆里分配多少存储空间, 也不必知道存储的数据要在堆里停留多长的 时间。因此,用堆保存数据时会得到更大的灵活性。要求创建一个对象时,只需 用 new 命令编制相关的代码即可。执行这些代码时,会在堆里自动进行数据的保 存。当然,为达到这种灵活性,必然会付出一定的代价:在堆里分配存储空间时 会花掉更长的时间!这是导致 Java 性能不佳的因素之一。 SUN 的 JVM 使用分代方式(Generation)管理 堆 空 间 , “代”分配给新旧对象 的内存池。这些对象的不断积累会导致一个的内存状态,从而推动垃圾收集的开 始。如图说明了 SUN 的 JVM 中堆空间粗略的划分。
如图所 示 , 年 轻的 一 代 包 括年 轻的 对 象空 间( eden )和 两 个存 活 (survivor)空间(SS#1 和 SS#2) 。新对象被分配到 eden 中,那些存活较久的 对象则会从年轻的一代转移到老一代中。图中的 Perm 段叫做永久代(permanent generation) ,它保存了 JVM 的类和方法对象。 (3) 静态存储(Static)。这儿的“静态”(Static)是指“位于固定位置” (尽管也在 RAM 里)或是有且仅有一份。程序运行期间,静态存储的数据将随时 等候调用。可用 static 关键字指出一个对象的特定元素是静态的。但 Java 对象 本身永远都不会置入静态存储空间。
2.2 对象在内存中的分配
了解了内存中这些存放数据的方式后, 我们先来看看 C++中存放对象的机制: 在 C++中,对象可以是在堆栈中创建的,这样可达到更快的速度。然而,在 C++ 里创建“内存堆”(Heap)对象通常会慢得多。这种内存堆实际是一个大的内存 池,要求必须进行再循环(再生) 。这里可以把 C++的 Heap 想象是一块场地,在 这里面中每个对象不断监视属于自己的地盘, 他们可能在以后的某个时刻不再继 续占用目前所占用的空间,即释放后的内存会在堆里留下一个洞,所以再调用
一、引言:
在面向对象的程序设计中,我们肯定是在不停的产生对象,而产生的每个对 象为了生存,都需要动用资源,尤其是内存资源。当对象不需要内存,就该加以 清除,是资源可被释放、可再被使用。在程序设计的环境中:典型的情况是我们 首先产生对象,比如使用 new 关键字来创建对象,同时返回该对象的一个引用 (C++中该引用可能是个指针) ,之后我们通过该引用使用这个对象,当我们不再 使用该对象, 或是该对象失去对它的所有引用时, 它就应该被摧毁。 在 C,C++ 或 其它语言中,程序员负责取消分配内存。有时,这是一件很困难的事情。因为你 并不总是事先知道内存应在何时被释放。当在系统中没有能够被分配的内存时, 可导致程序瘫痪,这种程序被称作具有内存漏洞。在 Java 编程语言中解除了程 序员取消分配内存的责任, 它可提供一种系统级线程以跟踪每一存储器的分配情 况。这种系统级线程就是垃圾收集机器,在 Java 虚拟机的空闲周期,垃圾收集 线程检查并释放那些可被释放的存储器。垃圾收集在 Java 技术程序的生命周期 中自动进行,它解除了取消分配内存的要求,并避免了存储器漏洞。然而,它的 运行时间却是非确定的,因此我们必须小心。
Java 垃圾收集机器-Garbage Collector
一、引言:·································································· 2 二、垃圾收集的基本原理:············································· 2 2.1 JVM 中内存的划分··············································· 2 2.2 对象在内存中的分配············································· 3 2.3 垃圾收集的基本原理与算法····································4 三、与垃圾收集器交互··················································· 7 3.1 FINALIZE ()方法·················································· 7
12
3.2.1 软引用········································································ 13 3.2.2 弱引用········································································ 14 3.2.3 虚引用········································································· 15
四、总结··································································· 16
1
Java 垃圾收集机器-Garbage Collector
Java 垃圾收集机器-Garbage Collector
[摘 要]:本文详细讨论了 Java 垃圾收集机制的基本原理,并结合 Java 语言特 性, 介绍利用 Java 垃圾收集机制提高程序的健壮性和性能的基本方法。 [关键词]:Garbage collector,Heap, Reference, 对象,JVM
3.1.1 Finalize()用处之一:观察··············Finalize()用处之二:再生································ 11
3.2
NG.REF 包··················································
3
Java 垃圾收集机器-Garbage Collector
new 的时候,存储分配机制必须进行某种形式的搜索,使新对象能够利用已经存 在的空洞,否则就会很快用光堆的存储空间。之所以内存堆的分配会在 C++里对 性能造成如此重大的性能影响,对可用内存的搜索正是一个重要的原因。所以创 建基于堆栈的对象要快得多。 而在 Java 中的内存堆(Heap)更像一条传送带:每次分配了一个新对象后, “Heap 指针”都会朝前移动,这意味着对象存储空间的分配可以达到非常快的 速度。因为“Heap 指针”只是单纯的往前移动至未经分配的区域,所以它与 C++ 的堆栈分配方式几乎是不相上下的(当然,在数据记录上会多花一些开销,但要 比搜索存储空间快多了) 。但是如果只是按那种方式分配,最终就要求进行大量 的内存页面交换(这对性能的发挥会产生巨大干扰) ,而且终究会用光内存,出 现内存分页错误 (page fault)。所以 Java 引入了“垃圾收集器”。它在收集“垃 圾”的同时,也负责重新紧密排列(compact)堆里的所有对象,消除内存空洞, 将“堆指针”移至尽可能靠近传送带开头的地方,远离发生(内存)分页错误的 地点。 垃圾收集器会重新安排所有东西, 使其成为一个高速、 无限自由的堆模型 , 同时游刃有余地分配存储空间。但是垃圾收集时的代价是非常高昂的,这也是导 致 Java 性能不佳的因素之一。
相关主题