gpt4 book ai didi

java - 使用终结器清理弱引用缓存?

转载 作者:行者123 更新时间:2023-12-04 03:42:20 25 4
gpt4 key购买 nike

假设我有一个由弱引用或软引用组成的缓存。

那些弱/软引用需要在某个时候关闭。

理想情况下,一旦对象被 GC 从缓存中移除,对象就应该被关闭。

使用终结器/清理器来关闭这些资源是否合适,同时仍在程序末尾循环访问缓存并手动关闭它们?

public void CachedObject implements AutoClosable{
private boolean open;//getter
public CachedObject{
//Create resource
open=true;
}
@Override
public void finalize(){
super.finalize();
if(open){
try{
close();
}catch(IllegalStateException e){
//Log
}
}
}
@Override
public void close(){
if(open){
//Close
open=false;
}else{
throw new IllegalStateException("already closed");
}
}
}
private WeakHashMap<CachedObject,Object> cache=new WeakHashMap<>();

public void close(){
//Executed when cache is not needed anymore, e.g. program termination
for(CachedObject cachedElement:cache){
if(cachedElement.isOpen()){
cachedElement.close();
}
}
}

最佳答案

一般来说,使用finalizer 是一个相当糟糕的主意;毕竟它被弃用是有原因的。我认为首先了解这种特殊方法的机制很重要works to begin with ,或者为什么 it takes two cycles实现终结器的对象将消失。总体思路是,这是不确定的,容易出错,并且您可能会遇到这种方法的意外问题。

清理某些东西的实际方法是使用try with resources(通过AutoCloseable),就像:

CachedObject cached = new CachedObject...
try(cached) {

}

但这并不总是一种选择,就像您的情况一样,很可能。我不知道您使用的是什么缓存,但我们在内部使用我们自己的缓存,它实现了一个所谓的移除监听器(我们的实现主要基于 guava 并进行了少量添加我们自己的)。那么你的缓存可能有相同的吗?如果没有,您是否可以换成一个可以?

如果两者都不是一个选项,则有 Cleaner API since java-9 .你可以阅读它,例如做这样的事情:

static class CachedObject implements AutoCloseable {

private final String instance;

private static final Map<String, String> MAP = new HashMap<>();

public CachedObject(String instance) {
this.instance = instance;
}

@Override
public void close() {
System.out.println("close called");
MAP.remove(instance);
}
}

然后尝试使用它,通过:

private static final Cleaner CLEANER = Cleaner.create();

public static void main(String[] args) {

CachedObject first = new CachedObject("first");
CLEANER.register(first, first::close);
first = null;
gc();
System.out.println("Done");

}

static void gc(){
for(int i=0;i<3;++i){
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(100));
System.gc();
}
}

简单吧?也错了。 apiNote 通过以下方式提到了这一点:

The cleaning action is invoked only after the associated object becomes phantom reachable, so it is important that the object implementing the cleaning action does not hold references to the object

问题是 Runnable(在 Cleaner::register 的第二个参数中)捕获了 first,现在持有对它。这意味着永远不会调用清洁。相反,我们可以直接遵循文档中的建议:

static class CachedObject implements AutoCloseable {

private static final Cleaner CLEANER = Cleaner.create();
private static final Map<String, String> MAP = new HashMap<>();
private final InnerState innerState;
private final Cleaner.Cleanable cleanable;

public CachedObject(String instance) {
innerState = new InnerState(instance);
this.cleanable = CLEANER.register(this, innerState);
MAP.put(instance, instance);
}

static class InnerState implements Runnable {

private final String instance;

public InnerState(String instance) {
this.instance = instance;
}

@Override
public void run() {
System.out.println("run called");
MAP.remove(instance);
}
}

@Override
public void close() {
System.out.println("close called");
cleanable.clean();
}
}

代码看起来有点复杂,但实际上并没有那么复杂。我们想做两件主要的事情:

  • 将清洁代码分离到一个单独的类中
  • 并且那个类不能引用我们正在注册的对象。这是通过没有从 InnerStateCachedObject 的引用并使其成为 static 来实现的。

所以,我们可以测试一下:

 public static void main(String[] args) {

CachedObject first = new CachedObject("first");
first = null;
gc();

System.out.println("Done");
System.out.println("Size = " + CachedObject.MAP.size());


}

static void gc() {
for(int i=0;i<3;++i){
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(100));
System.gc();
}
}

输出:

run called
Done
Size = 0

关于java - 使用终结器清理弱引用缓存?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65766061/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com