微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

很少使用WeakReference吗?

如何解决很少使用WeakReference吗?

我有一个其实例由基础平台初始化和使用的类。

class MyAttributeConverter implements AttributeConverter<XX,YY> {

    public YY convertToDatabaseColumn(XX attribute) { return null; }

    public XX convertToEntityAttribute(YY dbData) { return null; }
}

没错,我想我需要添加一些静态方法用作方法引用。

    private static MyAttributeConverter instance;

    // just a lazy-initialization;
    // no synchronization is required;
    // multiple instantiation is not a problem;
    private static MyAttributeConverter instance() {
        if (instance == null) {
            instance = new MyAttributeConverter();
        }
        return instance;
    }

    // do as MyAttributeConverter::toDatabaseColumn(xx)

    public static YY toDatabaseColumn(XX attribute) {
        return instance().convertToDatabaseColumn(attribute);
    }

    public static XX toEntityAttribute(YY dbData) {
        return instance().convertToEntityAttribute(attribute);
    }

仍然没有什么错(我相信),我不喜欢instance坚持上课,这就是为什么我要这样做。

    private static WeakReference<MyAttributeConverter> reference;

    public static <R> R applyInstance(Function<? super MyAttributeConverter,? extends R> function) {
        MyAttributeConverter referent;
        if (reference == null) {
            referent = new MyAttributeConverter();
            refernce = new WeakReference<>(referent);
            return applyInstance(function);
        }
        referent = reference.get();
        if (referent == null) {
            referent = new MyAttributeConverter();
            refernce = new WeakReference<>(referent);
            return applyInstance(function);
        }
        return function.apply(referent); // @@?
    }

我基本上什至不知道如何测试这段代码。对于我提出的每个问题可能有些含糊的问题,我深感抱歉。

  • 这是(正确/错误方法吗?
  • reference.get()惯用语中的function.apply可能是null吗?
  • 是否有可能出现内存泄漏等问题?
  • 我应该依靠SoftReference而不是WeakReference吗?

谢谢。

解决方法

请注意,

// multiple instantiation is not a problem;
private static MyAttributeConverter instance() {
    if (instance == null) {
        instance = new MyAttributeConverter();
    }
    return instance;
}

并不是线程安全的,因为它对instance字段进行了两次读取。它们中的每一个都可以感知是否由其他线程进行了更新。这意味着instance == null中的第一个读取操作可能会感知到另一个线程写入的新值,而return instance;中的第二个读取操作可能会得出先前的值,即null。因此,当多个线程同时执行该方法时,此方法可能返回null。这种情况很少见,但是这种方法并不安全。您需要一个局部变量,以确保测试和return语句使用相同的值。

// multiple instantiation is not a problem;
private static MyAttributeConverter instance() {
    MyAttributeConverter current = instance;
    if (current == null) {
        instance = current = new MyAttributeConverter();
    }
    return current;
}

仅当MyAttributeConverter字段仅使用final不变时,这仍然是安全的。否则,线程可能会返回由另一个线程创建的实例,该实例处于未完全构造的状态。

您可以使用简单的方法来确保其不受这些限制的安全性:

private static final MyAttributeConverter instance = new MyAttributeConverter();

private static MyAttributeConverter instance() {
    return instance;
}

这仍然很懒,因为类初始化仅在one of the specified triggers上发生,即方法instance()的第一次调用。


您对WeakReference的使用也遇到相同的问题。此外,尚不清楚为什么要在两个已经在局部变量中具有必需参数的点上求助于方法的递归调用。

正确的实现要简单得多:

private static WeakReference<MyAttributeConverter> reference;

public static <R> R applyInstance(
    Function<? super MyAttributeConverter,? extends R> function) {

    WeakReference<MyAttributeConverter> r = reference;
    MyAttributeConverter referent = r != null? r.get(): null;      
    if (referent == null) {
        referent = new MyAttributeConverter();
        reference = new WeakReference<>(referent);
    }
    return function.apply(referent);
}

但是在使用它之前,您应该重新考虑复杂的代码是否值得努力。您已经接受了在对象被垃圾回收时重建对象的需求,甚至有可能在并发调用中构造多个实例这一事实表明您知道这种构造将很便宜。当构造便宜时,您可能根本不需要缓存它的实例。

只需考虑

public static <R> R applyInstance(
    Function<? super MyAttributeConverter,? extends R> function) {

    return function.apply(new MyAttributeConverter());
}

至少值得一试,衡量应用程序的性能并将其与其他方法进行比较。

另一方面,该实例似乎并没有占用大量内存,也不拥有非内存资源。否则,您更担心多个实例飞来飞去的可能性。因此,另一个值得尝试和比较的变体是上面显示的一个变体,它使用static final字段进行了懒惰的类初始化,并且没有机会垃圾收集该小对象。


最后一个澄清。你问

reference.get()惯用语内的function.apply可能是null吗?

由于在reference.get()的评估中没有function.apply的调用,因此这种调用现在没有机会评估为null。该函数收到一个强引用,并且由于调用代码确保此强引用不是null,因此在调用null方法期间它永远不会变成apply

通常,垃圾回收器绝不会以使用强引用的代码会注意到差异的方式更改应用程序状态(将更多内存留在一边)。

但是,由于您特别询问了reference.get(),因此垃圾收集器可能会在对象上次使用后 regardless of method executions or local scopes收集对象。因此,当该方法不再使用对象时,在执行apply方法期间可以收集引用对象。 Runtime optimizations may allow this to happen earlier than you might guess by looking at the source code,因为看起来像对象的使用(例如,读取的字段)在运行时可能不会使用该对象(例如,由于该值已保存在CPU寄存器中,从而无需访问对象的内存)。如上所述,所有操作都不会改变该方法的行为。

因此,在执行reference.get()方法期间假想的apply原则上可以评估为null,但是没有理由担心{{ 1}}方法不变。 JVM将保留对象的内存,直到确保此方法正确执行所需的时间。

但是,这种解释只是为了完整性。如前所述,对于不持有昂贵资源的对象,不应使用弱引用或软引用。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。