java

位置:IT落伍者 >> java >> 浏览文章

Java的垃圾回收机制详解和调优


发布日期:2022年08月23日
 
Java的垃圾回收机制详解和调优

JVM的gc概述

gc即垃圾收集机制是指jvm用于释放那些不再使用的对象所占用的内存java语言并不要求jvm有gc也没有规定gc如何工作不过常用的jvm都有gc而且大多数gc都使用类似的算法管理内存和执行收集操作

在充分理解了垃圾收集算法和执行过程后才能有效的优化它的性能有些垃圾收集专用于特殊的应用程序比如实时应用程序主要是为了避免垃圾收集中断而大多数OLTP应用程序则注重整体效率理解了应用程序的工作负荷和jvm支持的垃圾收集算法便可以进行优化配置垃圾收集器

垃圾收集的目的在于清除不再使用的对象gc通过确定对象是否被活动对象引用来确定是否收集该对象gc首先要判断该对象是否是时候可以收集两种常用的方法是引用计数和对象引用遍历

引用计数

引用计数存储对特定对象的所有引用数也就是说当应用程序创建引用以及引用超出范围时jvm必须适当增减引用数当某对象的引用数为便可以进行垃圾收集

对象引用遍历

早期的jvm使用引用计数现在大多数jvm采用对象引用遍历对象引用遍历从一组对象开始沿着整个对象图上的每条链接递归确定可到达(reachable)的对象如果某对象不能从这些根对象的一个(至少一个)到达则将它作为垃圾收集在对象遍历阶段gc必须记住哪些对象可以到达以便删除不可到达的对象这称为标记(marking)对象

下一步gc要删除不可到达的对象删除时有些gc只是简单的扫描堆栈删除未标记的未标记的对象并释放它们的内存以生成新的对象这叫做清除(sweeping)这种方法的问题在于内存会分成好多小段而它们不足以用于新的对象但是组合起来却很大因此许多gc可以重新组织内存中的对象并进行压缩(compact)形成可利用的空间

为此gc需要停止其他的活动活动这种方法意味着所有与应用程序相关的工作停止只有gc运行结果在响应期间增减了许多混杂请求另外更复杂的 gc不断增加或同时运行以减少或者清除应用程序的中断有的gc使用单线程完成这项工作有的则采用多线程以增加效率

几种垃圾回收机制

标记-清除收集器

这种收集器首先遍历对象图并标记可到达的对象然后扫描堆栈以寻找未标记对象并释放它们的内存这种收集器一般使用单线程工作并停止其他操作

标记-压缩收集器

有时也叫标记-清除-压缩收集器与标记-清除收集器有相同的标记阶段在第二阶段则把标记对象复制到堆栈的新域中以便压缩堆栈这种收集器也停止其他操作

复制收集器

这种收集器将堆栈分为两个域常称为半空间每次仅使用一半的空间jvm生成的新对象则放在另一半空间中gc运行时它把可到达对象复制到另一半空间从而压缩了堆栈这种方法适用于短生存期的对象持续复制长生存期的对象则导致效率降低

增量收集器

增量收集器把堆栈分为多个域每次仅从一个域收集垃圾这会造成较小的应用程序中断

分代收集器

这种收集器把堆栈分为两个或多个域用以存放不同寿命的对象jvm生成的新对象一般放在其中的某个域中过一段时间继续存在的对象将获得使用期并转入更长寿命的域中分代收集器对不同的域使用不同的算法以优化性能

并发收集器

并发收集器与应用程序同时运行这些收集器在某点上(比如压缩时)一般都不得不停止其他操作以完成特定的任务但是因为其他应用程序可进行其他的后台操作所以中断其他处理的实际时间大大降低

并行收集器

并行收集器使用某种传统的算法并使用多线程并行的执行它们的工作在多cpu机器上使用多线程技术可以显着的提高java应用程序的可扩展性

Sun HotSpot

JVM堆大小的调整

Sun HotSpot 使用分代收集器它把堆分为三个主要的域新域旧域以及永久域Jvm生成的所有新对象放在新域中一旦对象经历了一定数量的垃圾收集循环后便获得使用期并进入旧域在永久域中jvm则存储class和method对象就配置而言永久域是一个独立域并且不认为是堆的一部分

下面介绍如何控制这些域的大小可使用Xms和Xmx 控制整个堆的原始大小或最大值

下面的命令是把初始大小设置为M

java –Xmsm

–Xmxm为控制新域的大小可使用XX:NewRatio设置新域在堆中所占的比例

下面的命令把整个堆设置成m新域比率设置成即新域与旧域比例为新域为堆的/M

java –Xmsm –Xmxm

–XX:NewRatio =可使用XX:NewSize和XX:MaxNewsize设置新域的初始值和最大值

下面的命令把新域的初始值和最大值设置成m:

java –Xmsm –Xmxm –Xmnm

永久域默认大小为m运行程序时jvm会调整永久域的大小以满足需要每次调整时jvm会对堆进行一次完全的垃圾收集

使用XX:MaxPerSize标志来增加永久域搭大小在WebLogic Server应用程序加载较多类时经常需要增加永久域的最大值当jvm加载类时永久域中的对象急剧增加从而使jvm不断调整永久域大小为了避免调整可使用XX:PerSize标志设置初始值

下面把永久域初始值设置成m最大值设置成m

java Xmsm Xmxm Xmnm XX:PermSize=m XX:MaxPermSize=m

默认状态下HotSpot在新域中使用复制收集器该域一般分为三个部分第一部分为Eden用于生成新的对象另两部分称为救助空间当Eden 充满时收集器停止应用程序把所有可到达对象复制到当前的from救助空间一旦当前的from救助空间充满收集器则把可到达对象复制到当前的to救助空间From和to救助空间互换角色维持活动的对象将在救助空间不断复制直到它们获得使用期并转入旧域使用XX:SurvivorRatio 可控制新域子空间的大小

同NewRation一样SurvivorRation规定某救助域与Eden空间的比值比如以下命令把新域设置成mEden占m每个救助域各占m

java Xmsm Xmxm Xmnm XX:SurvivorRation =

如前所述默认状态下HotSpot对新域使用复制收集器对旧域使用标记-清除-压缩收集器在新域中使用复制收集器有很多意义因为应用程序生成的大部分对象是短寿命的理想状态下所有过渡对象在移出Eden空间时将被收集如果能够这样的话并且移出Eden空间的对象是长寿命的那么理论上可以立即把它们移进旧域避免在救助空间反复复制但是应用程序不能适合这种理想状态因为它们有一小部分中长寿命的对象最好是保持这些中长寿命的对象并放在新域中因为复制小部分的对象总比压缩旧域廉价为控制新域中对象的复制可用XX:TargetSurvivorRatio控制救助空间的比例(该值是设置救助空间的使用比例如救助空间位M该值表示可用K)该值是一个百分比默认值是当较大的堆栈使用较低的 sruvivorratio时应增加该值到以更好利用救助空间XX:maxtenuring threshold可控制上限

为放置所有的复制全部发生以及希望对象从eden扩展到旧域可以把MaxTenuring Threshold设置成设置完成后实际上就不再使用救助空间了因此应把SurvivorRatio设成最大值以最大化Eden空间设置如下

java … XX:MaxTenuringThreshold= –XX:SurvivorRatio=

BEA JRockit JVM的使用

Bea WebLogic 使用的新的JVM用于Intel平台在Bea安装完毕的目录下可以看到有一个类似于jrockitsp__的文件夹这就是 Bea新JVM所在目录不同于HotSpot把Java字节码编译成本地码它预先编译成类JRockit还提供了更细致的功能用以观察JVM的运行状态主要是独立的GUI控制台(只能适用于使用Jrockit才能使用jrockitsp__自带的console监控一些cpu及 memory参数)或者WebLogic Server控制台

Bea JRockit JVM支持种垃圾收集器

分代复制收集器

它与默认的分代收集器工作策略类似对象在新域中分配即JRockit文档中的nursery这种收集器最适合单cpu机上小型堆操作

单空间并发收集器

该收集器使用完整堆并与背景线程共同工作尽管这种收集器可以消除中断但是收集器需花费较长的时间寻找死对象而且处理应用程序时收集器经常运行如果处理器不能应付应用程序产生的垃圾它会中断应用程序并关闭收集

分代并发收集器这种收集器在护理域使用排它复制收集器在旧域中则使用并发收集器由于它比单空间共同发生收集器中断频繁因此它需要较少的内存应用程序的运行效率也较高注意过小的护理域可以导致大量的临时对象被扩展到旧域中这会造成收集器超负荷运作甚至采用排它性工作方式完成收集

上一篇:java序列化--java.io.Serializable接口解析

下一篇:Java进阶:Java编写通过代理访问的应用程序