Java理论与实践: JVM 1.4.1中的垃圾收集 - 编程入门网
收集的根集中。有两种创建从老对象到年轻 对象的引用的方法。要么是将老对象中包含的引用修改为指向年轻对象,要么是 将引用其他年轻对象的年轻对象提升为更老的一代。
跟踪代间引用 不管一个老到年轻的引用是通过提升还是指针修改创建的,垃圾收集器在进 行小的收集时需要有全部老到年轻的引用。做到这一点的一种方法是跟踪老的代 ,但是这显然有很大的开销。更好的一种方法是线性扫描老的代以查找对年轻对 象的引用。这种方法比跟踪更快并有更好的区域性(locality),但是仍然有很 大的工作量。 赋值函数(mutator)和垃圾收集器可以共同工作以在创建老到年轻的引用时 维护它们的完整列表。当对象提升为更老一代时,垃圾收集器可以记录所有由于 这种提升而创建的老到年轻的引用,这样就只需要跟踪由指针修改所创建的代间 引用。 垃圾收集器可以有几种方法跟踪由于修改现有对象中的引用而产生的老到年 轻的引用。它可以使用在引用计数收集器中维护引用计数的同样方法(编译器可 以生成围绕指针赋值的附加指令)跟踪它们,也可以在老一代堆上使用虚拟内存 保护以捕获向老对象的写入。另一种可能更有效的虚拟内存方法是在老一代堆中 使用页修改脏位(page modification dirty bit),以确定为找到包含老到年 轻指针的对象时要扫描的块。 用一点小技巧,就可以避免跟踪每一个指针修改并检查它是否跨越代边界的 开销。例如,不需要跟踪针对本地或者静态变量的存储,因为它们已经是根集的 一部分了。也可以避免跟踪存储在某些构造函数中的指针,这些构造函数只用于 初始化新建对象的字段(即所谓 初始化存储(initializing stores)),因为 (几乎)所有对象都是分配到年轻代中。不管是什么情况,运行库都必须维护一 个老对象到年轻对象的引用集并在收集年轻代时将这些引用添加到根集中。 在图 1 中,箭头表示堆中对象间的引用。红色箭头表示必须添加到根集中供 小的收集使用的老到年轻的引用。蓝色箭头表示从根集或者年轻代到老对象的引 用,在只收集年轻代时不需要跟踪它们。 图 1. 代间引用 Java理论与实践: JVM 1.4.1中的垃圾收集(2)时间:2010-12-21 IBM Brian Goetz卡片标记 Sun JDK 使用一种称为 卡片标记(card marking)算法的改进算法以标识对 老一代对象的字段中包含的指针的修改。在这种方法中,堆分为一组 卡片,每 个卡片一般都小于一个内存页。JVM 维护着一个卡片映射,对应于堆中的每一个 卡片都有一个位(在某些实现中是一个字节)。每次修改堆中对象中的指针字段 时,就在卡片映射中设置对应那张卡片的相应位。在垃圾收集时,就对与老一代 中卡片相关联的标记位进行检查,对脏的卡片扫描以寻找对年轻代有引用的对象 。然后清除标记位。卡片标记有几项开销――卡片映射所需的额外空间、对每一 个指针存储所做的额外工作,以及在垃圾收集时做的额外工作。对每一个非初始 化堆指针存储,卡片标记算法可以只增加两到三个机器指令,并要求在小的收集 时对所有脏卡片上的对象进行扫描。 JDK 1.4.1 默认收集器 在默认情况下,JDK 1.4.1 将堆分为两部分,一个年轻的代和一个老的代( 实际上,还有第三部分――永久空间,它用于存储装载的类和方法对象)。借助 于复制收集器,年轻的代又分为一个创建空间(通常称为 Eden)和两个生存半 空间。 老的代使用标记-整理收集器。对象在经历了几次复制后提升到老的代。小的 收集将活的对象从 Eden 和一个生存半空间复制到另一个生存半空间,并可能提 升一些对象到老的代。大的收集(major collection)既会收集年轻的代,也会 收集老的代。System.gc() 方法总是触发一个大的收集,这就是应该尽量少用( 如果不能完全不用的话) System.gc() 的原因之一,因为大 |
凌众科技专业提供服务器租用、服务器托管、企业邮局、虚拟主机等服务,公司网站:http://www.lingzhong.cn 为了给广大客户了解更多的技术信息,本技术文章收集来源于网络,凌众科技尊重文章作者的版权,如果有涉及你的版权有必要删除你的文章,请和我们联系。以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢! |