如果大量使用,匿名内部类会对性能产生多大影响?

时间:2014-01-09 00:50:26

标签: java

===结论====

https://softwareengineering.stackexchange.com/a/149569找到了一个好的阅读状态

当前的GC算法实际上已经过优化,可用于创建许多短暂的小对象 所以我认为在项目中使用匿名内部类对于性能来说不是什么大问题

=============================================== =========================

因为函数不是当前Java(Java7)中的第一类公民,所以使用匿名内部类似乎是实现完整异步应用程序的唯一方法。

我知道它会在一定程度上带来更大的内存占用和垃圾收集器负担,但我不知道它有多严重?最近我的同事和我争论是因为我的代码是通过利用匿名内部类来编写的,他的反对意见完全取决于性能。虽然我不同意,但我不能举出任何证明自己的例子。我知道groovy正在使用匿名类实现闭包,但是groovy的性能比java差(当然匿名应该只承担部分责任,因为groovy也大量使用反射)。

所以我想在现实世界中,有没有任何项目因为性能而放弃匿名课程?像swing这样的UI框架怎么样?是否大量使用匿名类?

没有匿名,我无法想象如何在java中优雅地实现异步。我们的项目已经使用一种非常丑陋的方式使类方法作为函数指针工作。我讨厌那么多,并想说服人们匿名上课是正确的方法。

我的例子:

// basically, I use Completion interface to make normal java methods work in async manner

public interface Completion {
    void success();

    void fail(String reason);
}


void methodA(Completion completion) {

     do_some_business_by_calling_remote_service ....

     when remote_service_ack_success:
         completion.success();
     else:
         completion.fail(remote_service_error);

}

void methodB() {
    methodA(new Completion() {
       public void success() {
           continue to do something;
       }

       public void fail(String err) {
           handle error
       }
    });
}

1 个答案:

答案 0 :(得分:8)

这里基本上有两个问题,它们都与匿名方面无关。匿名类与常规内部类没有任何不同,只是它们没有名称。一个匿名的内部类被编译成一个常规的内部类,而这个内部类与静态的嵌套类实际上并没有什么不同。

问题1是因为它们是内在的,所以它们保留对封闭类的引用:

class Outer {
    interface Inner {}

    Inner inner = new Inner() {
        {
            System.out.println(Outer.this);
        }
    };
}

这不是一个问题,而且大部分时间都是需要的,因为你正在做一些功能性的事情,并希望在内部类中使用外部实例的成员。但它可能会产生问题,因为只要内部类存活,外部类就不能被垃圾收集。

问题2确实它们确实是一个对象,所以你的方法B每次调用它时都会创建一个新对象。

显而易见的解决方案就是创建一次:

class MyProcess {

    final Completion myCompletion = new Completion() {
        @Override
        public void success() {}
        @Override
        public void fail(String err) {}
    }

    void methodA(Completion c) {}

    void methodB() {
        methodA(myCompletion);
    }
}

看起来你喜欢的是语法,并没有真正的解决方案来保持语法而不是同时创建一个对象。

我的个人意见:如果你不是很多地调用这个方法,我同意语法可以很好而且清晰。如果你经常调用它,请切换到单个对象,因为你占用了内存空间。如果它被调用了1000次,那就是1000个对象。对象大小因平台而异,但通常至少为8或16个字节+指向外部实例的指针。这不是巨大的影响,但它可以,例如,提示垃圾收集运行,这可能会导致微妙的停滞。

顺便说一句,我再次考虑这个问题并想到了以下想法:

Completion myLazyCompletion;

void methodB() {
    methodA(myLazyCompletion != null ? myLazyCompletion :
        (myLazyCompletion = new Completion() {

            // overrides

        })
    );
}

我会说不要这样做,但我认为这很有意思。 :)