How huge would anonymous inner class impact performance if it is heavily used?
===结论====
在https://softwareengineering.stackexchange.com/a/149569上找到了很好的读物,其中指出
当前的GC算法实际上已经过优化,可以创建许多寿命很短的小对象,
因此,我认为在项目中大量使用匿名内部类对于性能而言不是什么大事*
================================================ =======================
因为函数不是当前Java(Java7)中的第一类公民,所以使用匿名内部类似乎是实现完整异步应用程序的唯一方法。
我知道它将在某种程度上带来更大的内存占用和负担垃圾收集器,但是我不知道它有多严重?最近,我的同事与我争论,因为我的代码是通过利用匿名内部类以功能样式编写的,他的反对全部是关于性能的。尽管我不同意,但我无法举任何例子来证明自己。我知道groovy都使用匿名类来实现闭包,但是groovy的性能确实比Java差(当然,匿名应该只承担一部分责任,因为groovy也大量使用反射)。
所以我想知道在现实世界中,是否有任何项目会因为性能而放弃匿名类?像秋千这样的UI框架又如何呢?是否大量使用匿名类?
没有匿名,我无法想象如何在Java中优雅地实现异步。我们的项目已经使用了非常丑陋的方法来使类方法作为函数指针工作。我非常讨厌,并希望说服人们匿名上课是正确的方法。
我的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | // 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是由于它们是内部的,因此它们保留对封闭类的引用:
1 2 3 4 5 6 7 8 9 | class Outer { interface Inner {} Inner inner = new Inner() { { System.out.println(Outer.this); } }; } |
这并不是什么大问题,大多数情况下并不是这样,因为您正在执行某些功能,并且希望在内部类中使用外部实例的成员。但是这可能会引起问题,因为只要内部类还活着,就不能对外部类进行垃圾回收。
问题2实际上是它们是一个对象,因此确实您的methodB每次被调用时都会创建一个新对象。
显而易见的解决方案是只创建一次:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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个字节。虽然影响不大,但是例如可以迅速运行垃圾收集,这可能导致微妙的停滞。
顺便说一句,我又在考虑这个问题,并想到了以下想法:
1 2 3 4 5 6 7 8 9 10 11 | Completion myLazyCompletion; void methodB() { methodA(myLazyCompletion != null ? myLazyCompletion : (myLazyCompletion = new Completion() { // overrides }) ); } |
我会说不要这样做,但是我认为这很有趣。 :)