文章采集调用(Java™清洁工程师BrianGoetz探究了弱引用(weakreferences)问题)
优采云 发布时间: 2021-09-19 16:07文章采集调用(Java™清洁工程师BrianGoetz探究了弱引用(weakreferences)问题)
在Java理论与实践的前一期文章中,Java™ 清理工程师Brian Goetz探索弱引用,它允许您警告垃圾采集器您希望维护对对象的引用,而不阻止对象被垃圾采集。在本期文章中,他将解释另一种形式的引用对象,软引用,以帮助垃圾采集器管理内存使用并消除潜在的内存泄漏
垃圾采集可以防止Java程序内存泄漏,至少对于“内存泄漏”的狭义定义来说是这样,但这并不意味着我们可以完全忽略Java程序中的对象生存期问题。当我们没有对对象生命周期给予足够的关注或打破了管理对象生命周期的标准机制时,Java程序中经常会发生内存泄漏。例如,上次我们看到,在尝试将元数据与临时对象关联时,未能划分对象的生命周期可能会导致意外的对象保留。还有其他类似的情况会忽略或破坏对象生命周期管理,并导致内存泄漏
对象分离
清单1中的leakychecksum类说明了一种形式的内存泄漏,有时称为对象游荡,其中使用getfilechecksum()方法计算文件内容的校验和。getfilechecksum()方法将文件内容读入缓冲区以计算校验和。更直观的实现只是在getfilechecksum()中将缓冲区作为局部变量分配,但是这个版本比那个版本更“智能”,而不是在实例字段中缓存缓冲区以减少内存流失。这种“优化”通常不会带来预期的效益;对象分配比许多人预期的要便宜。(还要注意,将缓冲区从局部变量提升到实例变量会使类在没有额外同步的情况下不再是线程安全的。直观的实现不需要将getfilechecksum()声明为已同步,并且在同时调用时将提供更好的可伸缩性。)
清单1.显示了“对象分离”类
//BADCODE-DONOTE公式
publicclassLeakyChecksum{
privatebyte[]字节数组
PublicSynchronizedEntGetFileChecksum(StringfileName){
intlen=getFileSize(文件名)
if(bytearray==null | | bytearray.Length)管理。后一个选项通常更好,因为它为垃圾采集器带来更少的工作,并允许在特别需要内存时以更少的工作回收整个缓存。弱引用有时被错误地用于替换软引用来构建缓存,但这将导致缓存性能差。实际上,弱引用会在图像变弱后(通常在再次使用缓存对象之前)迅速清除它们吗?因为小型垃圾采集经常运行
对于性能严重依赖缓存的应用程序,软引用是一种无用的手段。它确实无法取代复杂的缓存框架,该框架可以提供灵活的终止、复制和事务缓存。但是,作为一种“廉价且肮脏”的缓存机制,降低价格非常有吸引力
与弱引用一样,还可以使用关联的引用队列创建软引用,该队列在垃圾回收器清除时进入队列。引用队列对于软引用没有弱引用有用,但它们可以用于发出应用程序内存开始耗尽的管理警报
垃圾采集器如何处理引用
弱引用和软引用都扩展了抽象引用类(幻影引用也是如此,稍后将在文章中描述)。引用对象由垃圾回收器以特殊方式处理。当垃圾回收器在跟踪堆期间遇到引用对象时,它不会标记或跟踪引用对象,而是将引用放置在已知活动引用对象的队列上。在跟踪之后,垃圾回收器会识别软访问对象吗?除软引用外,这些对象上没有强引用。垃圾回收器根据当前采集回收的内存总量和其他策略注意事项确定此时是否需要清除软引用。如果要清除的软引用具有相应的引用队列,则y将进入队列。剩余的软访问对象(未清除的对象)将被视为根集,堆跟踪将继续使用这些新根,以便可以标记通过活动软引用访问的对象
处理软引用后,将标识弱可及对象的集合?此类对象上没有强引用或软引用。这些对象将被清除并排队。所有引用类型在排队之前都会被清除,因此将进行事后处理。清除的线程将永远无法访问引用对象,但t仅指向引用对象。因此,当引用与引用队列一起使用时,通常需要细分适当的引用类型,并在设计中直接使用它(如weakhashmap,其map.entry扩展了WeakReference)或存储对需要清除的实体的引用
参考处理的性能成本
引用对象会给垃圾采集过程带来一些额外的成本。对于每个垃圾采集,必须构造一个活动引用对象的列表,并且必须适当地处理每个引用,这会增加每个采集的每个引用的一些开销,而不管此时是否采集引用引用对象本身接受垃圾采集,并且可以在引用对象之前进行采集,在这种情况下,它们不会添加到队列中
基于数组的集合
当使用数组实现堆栈或环形缓冲区等数据结构时,会出现另一种形式的对象分离在该方法中,在顶部指针递减后,元素仍然保留对将弹出堆栈的对象的引用。这意味着对该对象的引用仍然可以被程序访问(即使程序实际上不会使用该引用),它防止对象被垃圾采集,直到该位置被future push()重用为止
在基于数组的集合中列出3.对象
publicclassLeakyStack{
privateObject[]元素=新对象[MAX_元素]
privateintsize=0
publicvoidpush(Objecto){elements[size++]=o;}
publicObjectpop(){
如果(大小==0)
thrownewmptystackexception()
否则{
Objectresult=元素[--size]
//元素[大小+1]=空
返回结果
}
}
}
结论
与弱引用一样,软引用通过利用垃圾采集器在做出缓存采集决策时的帮助,有助于防止应用程序中的对象漂移。只有当应用程序能够容忍大量软引用对象时,软引用才适用
参考资料:
您可以在developerWorks全球网站上参考本文的英文原文
“Java理论与实践:用弱引用阻止内存泄漏”:上一期《Java理论与实践》介绍了类似的软引用
“关注性能:调优垃圾回收”:kirkpepperdine和JackShirazi证明,即使是缓慢的内存泄漏最终也会给垃圾回收器带来巨大压力
引用对象和垃圾采集:本文由sun在引用对象添加到类库后不久撰写,描述垃圾采集器如何处理引用对象
Java理论与实践:Brian Goetz的完整系列
Java技术专区:数百篇关于Java编程文章各个方面的文章@
获得产品和技术
Jtune:这个免费的Jtune工具利用GC日志,以图形方式显示堆大小、GC持续时间和其他有用的内存管理数据