有些垃圾收集专用于特殊的应用程序。
比如,实时应用程序主要是为了避免垃圾收集中断,而大多数 OLTP 应用程序则注重整体效率。
理解了应用程序的工作负荷和 JVM 支持的垃圾收集算法,便可以进行优化配置垃圾收集器。
垃圾收集的目的在于清除不再使用的对象。
GC 通过确定对象是否被活动对象引用来确定是否收集该对象。
GC 首先要判断该对象什么时候可以收集。
两种常用的方法是引用计数和对象引用遍历。
引用计数存储对特定对象的所有引用数,也就是说,当应用程序创建引用以及引用超出范围时, JVM 必须适当增减引用数。
当某对象的引用数为 0 时,便可以进行垃圾收集。
早期的 JVM 使用引用计数,现在大多数 JVM 采用对象引用遍历。
对象引用遍历从一组对象开始,沿着整个对象图上的每条链接,递归确定可到达( reachable )的对象。
如果某对象不能从这些根对象的一个(至少一个)到达,则将它作为垃圾收集。
在对象遍历阶段, GC 必须记住哪些对象可以到达,以便删除不可到达的对象,这称为标记( marking )对象。
下一步, GC 要删除不可到达的对象。
删除时,有些 GC 只是简单的扫描堆栈,删除未标记的对象,并释放它们的内存以生成新的对象,这叫做清除( sweeping )。
这种方法的问题在于内存会分成好多小段,而它们不足以用于新的对象,但是组合起来却很大。
因此,许多 GC 可以重新组织内存中的对象,并进行压缩( compact ),形成可利用的空间。
为此, GC 需要停止其他的活动。
这种方法意味着所有与应用程序相关的工作停止,只有 GC 运行。
结果,在响应期间增减了许多混杂请求。
另外,更复杂的 GC 不断增加或同时运行以减少或者清除应用程序的中断。
有的 GC 使用单线程完成这项工作,有的则采用多线程以增加效率。
下面列举一些 JVM 使用的 GC
标记-清除收集器:这种收集器首先遍历对象图并标记可到达的对象,然后扫描堆栈以寻找未标记对象并释放它们的内存。这种收集器一般使用单线程工作并停止其他操作。
标记-压缩收集器:有时也叫标记-清除-压缩收集器,与标记-清除收集器有相同的标记阶段。在第二阶段,则把标记对象复制到堆栈的新域中以便压缩堆栈。这种收集器也停止其他操作。
复制收集器:将堆栈分为两个域,常称为半空间。每次仅使用一半的空间, JVM 生成的新对象则放在另一半空间中。 GC 运行时,它把可到达对象复制到另一半空间,从而压缩了堆栈。这种方法适用于短生存期的对象,持续复制长生存期的对象则导致效率降低。
增量收集器:把堆栈分为多个域,每次仅从一个域收集垃圾。这会造成较小的应用程序中断。有多种方法可以定义实际的 GC 。
分代收集器 : 把堆栈分为两个或多个域,用以存放不同寿命的对象。 JVM 生成的新对象一般放在其中的某个域中。过一段时间,继续存在的对象将获得使用期并转入更长寿命的域中。分代收集器对不同的域使用不同的算法以优化性能。
并发收集器 : 并发收集器与应用程序同时运行。这些收集器在某点上一般都不得不停止其他操作以完成特定的任务,但是因为其他应用程序可进行其他的后台操作,所以中断其他处理的实际时间大大降低。
并行收集器 : 并行收集器使用某种传统的算法并使用多线程并行的执行它们的工作。在多 cpu 机器上使用多线程技术可以显著的提高 java 应用程序的可扩展性。
Sun Hotspot <?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />
Sun Hotspot
可使用 -Xms 和 -Xmx 控制整个堆的原始大小或最大值。比如,下面的命令是把初始大小设置为 128M :
java –Xms
为控制新域的大小,可使用 -XX:NewRatio 设置新域在堆中所占的比例。比如下面的命令把整个堆设置成 128m ,新域比率设置成 3 ,即新域与旧域比例为 1 : 3 ,新域为堆的 1/4 或 32M :
java –Xms
可使用 -XX:NewSize 和 -XX:MaxNewsize 设置新域的初始值和最大值。比如,下面的命令把新域的初始值和最大值设置成 64m:
java –Xms
一般不把永久域当作堆的一部分。永久域默认大小为 4m 。运行程序时, JVM 会调整永久域的大小以满足需要。每次调整时, JVM 会对堆进行一次完全的垃圾收集。使用 -XX:MaxPerSize 标志来增加永久域搭大小。在 WebLogic Server 应用程序加载较多类时,经常需要增加永久域的最大值。当 JVM 加载类时,永久域中的对象急剧增加,从而使 JVM 不断调整永久域大小。为了避免调整,可使用 -XX:Per m Size 标志设置初始值。比如,下面把永久域初始值设置成 32m ,最大值设置成 64m 。
java –Xms
默认状态下, HotSpot 在新域中使用复制收集器。该域一般分为三个部分。第一部分为 Eden ,用于生成新的对象。另两部分称为救助空间,当 Eden 充满时,收集器停止应用程序,把所有可到达对象复制到当前的 from 救助空间,一旦当前的 from 救助空间充满,收集器则把可到达对象复制到当前的 to 救助空间。 From 和 to 救助空间互换角色。维持活动的对象将在救助空间不断复制,直到它们获得使用期并转入旧域。
使用 -XX:SurvivorRatio 可控制新域子空间的大小。同 NewRation 一样, SurvivorRation 规定某救助域与 Eden 空间的比值。比如,以下命令把新域设置成 64m , Eden 占 32m ,每个救助域各占 16m :
java –Xms
如前所述,默认状态下 HotSpot 对新域使用复制收集器,对旧域使用标记-清除-压缩收集器。在新域中使用复制收集器有很多意义,因为应用程序生成的大部分对象是短寿命的。理想状态下,所有过渡对象在移出 Eden 空间时将被收集。如果能够这样的话,并且移出 Eden 空间的对象是长寿命的,那么理论上可以立即把它们移进旧域,避免在救助空间反复复制。
但是,应用程序不能适合这种理想状态,因为它们有一小部分中长寿命的对象。最好是保持这些中长寿命的对象并放在新域中,因为复制小部分的对象总比压缩旧域廉价。
为控制新域中对象的复制,可用 -XX:TargetSurvivorRatio 控制救助空间的比例。该值是一个百分比,默认值是 50 。当较大的堆栈使用较低的 sruvivorratio 时,应增加该值到 80 至 90 ,以更好利用救助空间。
用 -XX:maxtenuring threshold 可控制上限。为放置所有的复制全部发生以及希望对象从 eden 扩展到旧域,可以把 MaxTenuring Threshold 设置成 0 。设置完成后,实际上就不再使用救助空间了,因此应把 SurvivorRatio 设成最大值以最大化 Eden 空间,设置如下:
java … -XX:MaxTenuringThreshold=0 –XX:SurvivorRatio = 5000
从 JVM 中获取信息以助于调整方案
-verbose.gc 开关可显示 GC 的操作内容。打开它,可以显示最忙和最空闲收集行为发生的时间、收集前后的内存大小、收集需要的时间等。
打开 -xx:+ printgcdetails 开关,可以详细了解 GC 中的变化。
打开 -XX: + PrintGCTimeStamps 开关,可以了解这些垃圾收集发生的时间,自 JVM 启动以后以秒计量。
最后,通过 -xx: + PrintHeapAtGC 开关了解堆的更详细的信息。
为了了解新域的情况,可以通过 -XX:=PrintTenuringDistribution 开关了解获得使用期的对象权。
BEA JRockit JVM 的使用
Bea WebLogic 8.1 使用的新的 JVM 用于 Intel 平台。在 Bea 安装完毕的目录下可以看到有一个类似于 jrockit81sp1_141_03 的文件夹。这就是 Bea 新 JVM 所在目录。
不同于 HotSpot 把 Java 字节码编译成本地码,它预先编译成类。 JRockit 还提供了更细致的功能用以观察 JVM 的运行状态,主要是独立的 GUI 控制台或者 WebLogic Server 控制台。 Bea JRockit JVM 支持 4 种垃圾收集器:
分代复制收集器:它与默认的分代收集器工作策略类似。对象在新域中分配,即 JRockit 文档中的 nursery ↑返回目录
前一篇: 全面剖析Java 6中新型模态对话框API
后一篇: 内存泄漏,走开 轻松搞定Java内存泄漏