min*_*das 8 java finalize finalizer soft-references
我正在使用一个搜索库,建议保持搜索句柄对象打开,这样可以使查询缓存受益.随着时间的推移,我发现缓存容易变得臃肿(几百兆并且不断增长),并且OOM开始启动.没有办法强制执行此缓存的限制,也没有计划它可以使用多少内存.所以我增加了Xmx限制,但这只是问题的临时解决方案.
最后,我想使这个对象所指的java.lang.ref.SoftReference.因此,如果系统在可用内存上运行不足,它将让对象运行并根据需要创建一个新对象.这会在新开始后降低一些速度,但这比击中OOM要好得多.
我看到的关于SoftReferences的唯一问题是没有干净的方式让他们的指示物最终确定.在我的情况下,在销毁搜索句柄之前我需要关闭它,否则系统可能会用完文件描述符.显然,我可以将这个句柄包装到另一个对象中,在其上写一个终结器(或挂钩到ReferenceQueue/PhantomReference)然后松开.但是,嘿,这个星球上的每篇文章都建议不要使用终结器,特别是 - 针对释放文件句柄的终结器(例如Effective Java ed.II,第27页).
所以我有些困惑.我应该小心地忽略所有这些建议并继续.否则,还有其他可行的替代方案吗?提前致谢.
编辑#1:根据Tom Hawtin的建议测试了一些代码后添加了下面的文字.对我来说,似乎任何一个建议都没有用,或者我错过了一些东西.这是代码:
class Bloat { // just a heap filler really
private double a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;
private final int ii;
public Bloat(final int ii) {
this.ii = ii;
}
}
// as recommended by Tom Hawtin
class MyReference<T> extends SoftReference<T> {
private final T hardRef;
MyReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
this.hardRef = referent;
}
}
//...meanwhile, somewhere in the neighbouring galaxy...
{
ReferenceQueue<Bloat> rq = new ReferenceQueue<Bloat>();
Set<SoftReference<Bloat>> set = new HashSet<SoftReference<Bloat>>();
int i=0;
while(i<50000) {
// set.add(new MyReference<Bloat>(new Bloat(i), rq));
set.add(new SoftReference<Bloat>(new Bloat(i), rq));
// MyReference<Bloat> polled = (MyReference<Bloat>) rq.poll();
SoftReference<Bloat> polled = (SoftReference<Bloat>) rq.poll();
if (polled != null) {
Bloat polledBloat = polled.get();
if (polledBloat == null) {
System.out.println("is null :(");
} else {
System.out.println("is not null!");
}
}
i++;
}
}
Run Code Online (Sandbox Code Playgroud)
如果我使用-Xmx10m和SoftReferences 运行上面的代码片段(如上面的代码中所示),我将获得大量的is null :(打印.但是如果我用MyReference(用MyReference取消注释两行并用SoftReference注释出来的话)替换代码,我总是得到OOM.
正如我从建议中所理解的那样,在内部进行硬性参考MyReference不应该阻止物体撞击ReferenceQueue,对吗?
对于有限数量的资源:子类SoftReference.软引用应指向封闭对象.子类中的强引用应引用资源,因此始终可以很容易地访问它.通过读取ReferenceQueue poll资源可以关闭并从缓存中删除.缓存需要正确释放(如果SoftReference自身被垃圾收集,则无法排入a ReferenceQueue).
请注意,您只在缓存中释放了有限数量的资源 - 逐出旧条目(实际上,如果符合您的情况,您可以使用有限缓存丢弃软引用).通常情况是非存储器资源更重要,在这种情况下,没有外来参考对象的LRU驱逐缓存就足够了.
(我的回答#1000.发自伦敦DevDay.)
汤姆斯答案是正确答案,但是问题中添加的代码与汤姆提出的代码不同.汤姆提出的建议看起来更像是这样的:
class Bloat { // just a heap filler really
public Reader res;
private double a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;
private final int ii;
public Bloat(final int ii, Reader res) {
this.ii = ii;
this.res = res;
}
}
// as recommended by Tom Hawtin
class MySoftBloatReference extends SoftReference<Bloat> {
public final Reader hardRef;
MySoftBloatReference(Bloat referent, ReferenceQueue<Bloat> q) {
super(referent, q);
this.hardRef = referent.res;
}
}
//...meanwhile, somewhere in the neighbouring galaxy...
{
ReferenceQueue<Bloat> rq = new ReferenceQueue<Bloat>();
Set<SoftReference<Bloat>> set = new HashSet<SoftReference<Bloat>>();
int i=0;
while(i<50000) {
set.add(new MySoftBloatReference(new Bloat(i, new StringReader("test")), rq));
MySoftBloatReference polled = (MySoftBloatReference) rq.poll();
if (polled != null) {
// close the reference that we are holding on to
try {
polled.hardRef.close();
} catch (IOException e) {
e.printStackTrace();
}
}
i++;
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,最大的区别在于硬引用是指需要关闭的对象.周围的对象可以并且将被垃圾收集,因此您不会点击OOM,但是您仍然有机会关闭引用.一旦你离开循环,那也将被垃圾收集.当然,在现实世界中,您可能不会成为res公共实例成员.
也就是说,如果您持有开放文件引用,那么在内存不足之前,您将面临完全没用的风险.您可能还希望拥有一个LRU缓存,以确保您不会仅仅在空中打开500个文件.它们也可以是MyReference类型,因此如果需要它们也可以被垃圾收集.
为了澄清MySoftBloatReference的工作原理,基类(即SoftReference)仍保留对占用所有内存的对象的引用.这是您需要释放以防止OOM发生的对象.但是,如果释放了该对象,您仍然需要释放Bloat正在使用的资源,也就是说,Bloat正在使用两种类型的资源,内存和文件句柄,这两种资源都需要被释放,或者您运行一个或另一个资源.SoftReference通过释放该对象来处理内存资源的压力,但是您还需要释放其他资源即文件句柄.由于Bloat已被释放,我们无法使用它来释放相关资源,因此MySoftBloatReference会保留对需要关闭的内部资源的硬引用.一旦被告知Bloat已被释放,即一旦参考在ReferenceQueue中出现,那么MySoftBloatReference也可以通过它具有的硬引用关闭相关资源.
编辑:更新了代码,以便在抛入类时进行编译.它使用StringReader来说明如何关闭Reader的概念,Reader用于表示需要释放的外部资源.在这个特殊情况下,关闭该流实际上是一个无操作,因此不需要,但它显示了如果需要它如何这样做.
| 归档时间: |
|
| 查看次数: |
2583 次 |
| 最近记录: |