反复实例化一个匿名类是浪费吗?

时间:2013-10-31 23:24:53

标签: java

我对以下风格的代码进行了评论:

Iterable<String> upperCaseNames = Iterables.transform(
    lowerCaseNames, new Function<String, String>() {
        public String apply(String input) {
            return input.toUpperCase();
        }
    });

这个人说每次我通过这段代码,我都会实例化这个匿名的Function类,而我宁愿在一个静态变量中有一个实例:

static Function<String, String> toUpperCaseFn =
    new Function<String, String>() {
        public String apply(String input) {
            return input.toUpperCase();
        }
    };
...
Iterable<String> upperCaseNames =
    Iterables.transform(lowerCaseNames, toUpperCaseFn);

在一个非常肤浅的层面上,这在某种程度上是有道理的;多次实例化一个类必须浪费内存或其他东西,对吗?

另一方面,人们在代码中间实例化匿名类,就像没有明天一样,编译器优化它是微不足道的。

这是一个有效的问题吗?

3 个答案:

答案 0 :(得分:7)

有关Sun / Oracle JVM优化的有趣事实,如果您实例化一个未在线程外部传递的对象,JVM将在堆栈而不是堆上创建对象。

通常,堆栈分配与暴露内存模型的语言相关联,如C ++。您不必在C ++中使用delete堆栈变量,因为在退出作用域时它们会自动释放。这与堆分配相反,堆分配要求您在完成后删除指针。

在Sun / Oracle JVM中,分析字节码以确定对象是否可以“逃避”该线程。有three levels of escape

  1. 无转义 - 该对象仅在创建它的方法/范围内使用,并且无法在该线程外访问该对象。
  2. 本地/ Arg转义 - 该对象由创建它的方法返回或传递给它调用的方法,但当前堆栈跟踪中的所有方法都不会将该对象放在可以在其外部访问的某个位置。线程。
  3. 全局转义 - 将对象放在可以在另一个线程中访问的位置。
  4. 这基本上类似于问题,1)我传递/返回它,2)我是否将它与附加到GC根的内容相关联?在您的特定情况下,匿名对象将被标记为“没有本地转义”并将被分配到堆栈,然后通过在{{的每次迭代时弹出堆栈来清除1}}循环,所以清理它会非常快。抱歉,当我写完答案时,我并没有太多关注。它实际上是本地转义,这意味着对象的任何锁定(读取:使用for)都将被优化掉。 (为什么要同步一些不会在另一个线程中使用的东西?)这与“无法逃避”不同,在堆栈上进行分配。重要的是要注意这个“分配”与堆分配不同。它真正做的是在堆栈上为非转义对象内的所有变量分配空间。如果在no-escape对象中有3个字段synchronizedintString,则将分配三个堆栈变量:MyObject,一个{{1引用和int引用。然后优化对象分配,并使用本地堆栈变量而不是堆变量来运行构造函数/方法。

    话虽如此,这对我来说听起来不成熟。除非后来证明代码很慢并且导致性能问题,否则您不应该做任何事情来降低其可读性。对我来说,这段代码非常易读,我会不管它。当然,这是完全主观的,但“性能”不是改变代码的好理由,除非它与算法运行时间有关。通常,过早优化是中级编码器的指标。 “专家”(如果有这样的事情)只是编写更容易维护的代码。

答案 1 :(得分:1)

简答:不 - 别担心。

答案很长:这取决于你实例化它的频率。如果在频繁调用的紧密循环中,可能 - 尽管注意到在应用函数时它会为String.toUpperCase()中的每个项调用Iterable一次 - 每个调用可能会创建一个新的String,这将产生更多的GC流失。

“过早优化是所有邪恶的根源” - Knuth

答案 2 :(得分:1)

找到这个帖子:Java anonymous class efficiency implications,你可能会发现它很有趣

做了一些微观基准测试。微基准测试是:每个循环迭代实例化一个(静态内部)类,一次实例化(静态内部)类并在循环中使用它,以及两个类似但具有匿名类的类。对于微基准测试,编译器似乎从循环中提取匿名类,并且如预测的那样,将匿名类提升为调用者的内部类。这意味着所有四种方法在速度上都难以区分。我也将它与外部课程进行了比较,并将其与同样的速度相提并论具有匿名类的那个可能需要大约128位的空间

您可以在http://jdmaguire.ca/Code/Comparing.java&amp ;;查看我的微观基准。 http://jdmaguire.ca/Code/OutsideComp.java。我在wordLen,sortTimes和listLen的各种值上运行它。同样,JVM的预热很慢,所以我改变了方法调用。请不要因为糟糕的非评论代码来判断我。我编程比RL更好。而微小的标记几乎和过早优化一样邪恶无用。

相关问题