java - 重复实例化匿名类是否浪费?

标签 java

我对一段代码的评论是这样的:

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

那个人说每次我浏览这段代码时,我都会实例化这个匿名函数类,我宁愿在静态变量中有一个实例:

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

从表面上看,这在某种程度上是有道理的;多次实例化一个类肯定会浪费内存什么的,对吧?

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

这是一个合理的担忧吗?

最佳答案

关于 Hot Spot JVM 优化的有趣事实,如果您实例化一个未在当前方法之外传递的对象,JVM 将在字节码级别执行优化。

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

在 Hot Spot JVM 中,分析字节码以确定对象是否可以“逃离”线程。有three levels of escape :

  1. 无转义 - 该对象仅在其创建的方法/范围内使用,不能在线程外访问该对象。
  2. Local/Arg escape - 对象由创建它的方法返回或传递给它调用的方法,但这些方法都不会将该对象放在可以在线程外访问的地方。
  3. 全局转义 - 对象被放置在可以在另一个线程中访问的地方。

这基本上类似于以下问题:1) 我是否将它传递给另一个方法或返回它,以及 2) 我是否将它与附加到 GC 根的某些东西相关联,例如 ClassLoader 或其他东西存储在 static 字段中?

在您的特定情况下,匿名对象将被标记为“本地转义”,这仅意味着对象上的任何锁(读取:同步 的使用)将被优化掉。 (为什么要在另一个线程中永远不会使用的东西上进行同步?)这与“无逃逸”不同,后者在堆栈上进行分配。请务必注意,此“分配”与堆分配不同。它真正做的是在堆栈上为非转义对象内的所有变量分配空间。如果在 no-escape 对象中有 3 个字段,intStringMyObject,那么将分配三个堆栈变量:一个 int,一个 String 引用,和一个 MyObject referenceMyObject 实例本身将仍然存储在堆中,除非它也被分析为“无法逃脱”。然后优化对象分配,构造函数/方法将使用本地堆栈变量而不是堆变量运行。

话虽这么说,但对我来说这听起来像是过早的优化。除非代码后来被证明速度很慢并且导致性能问题,否则您不应该做任何事情来降低它的可读性。对我来说,这段代码可读性很强,我不会管它。当然,这完全是主观的,但是“性能”不是更改代码的好理由,除非它与实际运行时间有关。通常,过早的优化会导致代码更难维护,性能优势微乎其微。

Java 8+ 和 Lambdas

如果分配匿名实例仍然困扰您,我建议您改用 Lambdas 来处理单一抽象方法 (SAM) 类型。 Lambda 评估是使用 invokedynamic 执行的,并且实现最终在第一次调用时仅创建 Lambda 的单个实例。可以找到更多详细信息in my answer herethis answer here .对于非 SAM 类型,您仍然需要分配一个匿名实例。在大多数用例中,这里的性能影响可以忽略不计,但在我看来,这种方式更具可读性。

引用资料

关于java - 重复实例化匿名类是否浪费?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19718353/

相关文章:

java - 具有 Spring session 和安全性的 Spring Boot。 RMI 失败并返回 401

java - JNI 与 Runtime.exec()

java - 修复 POODLE 问题,需要有关 SSLContext.getInstance ("TLS"的帮助)

java - 梅文。在模块中包含刚刚使用的其他依赖模块的库

java - Sqlite 查询检查 - 小于和大于

java - 用于多种 Activity 的全局 db4o 数据库

java - 如何将书籍的引用转换为 XML?

java - Java中子类可以直接调用父类的实例方法吗?

java - 使用 StAX 修改一个 XML 文件

java - vert.x 应用程序的限制