gpt4 book ai didi

java - 提取到 static final 是 Java 优化所必需的吗?

转载 作者:搜寻专家 更新时间:2023-10-31 20:18:21 25 4
gpt4 key购买 nike

考虑这个方法:

private void iterate(List<Worker> workers) {
SortedSet<Worker> set = new TreeSet<>(new Comparator<Worker>() {
@Override
public int compare(Worker w0, Worker w1) {
return Double.compare(w0.average, w1.average);
}
});

// ...
}

如您所见,集合正在创建一个带有自定义比较器的新 TreeSet

我想知道从性能/内存/垃圾收集/任何观点来看是否有任何区别,如果我这样做而不是污染外层空间:

static final Comparator<Worker> COMPARATOR = new Comparator<Worker>() {
@Override
public int compare(Worker w0, Worker w1) {
return Double.compare(w0.average, w1.average);
}
};

private void iterate(List<Worker> workers) {
SortedSet<Worker> set = new TreeSet<>(COMPARATOR);
// ...
}

我问的原因是,我觉得编译器应该已经解决了这个问题并为我优化了它,所以我不应该提取它,对吧?

同样的事情也适用于在方法中声明的字符串或其他临时的、不可变 对象。

将它提取为 final 变量会有什么不同吗?

注意:我知道这对性能提升的影响很小。问题是是否存在任何差异,尽管可以忽略不计。

最佳答案

肯定会有区别的。

  • CPU 影响:分配给静态减少了每次分配新比较器所需的工作量
  • GC效果:每次分配一个新对象,然后立即丢弃,不会有young GC成本;然而,将它分配给一个变量会增加 GC 时间(非常非常小),因为它是一组额外的需要遍历的引用。死对象不花钱,活对象需要。
  • 内存效应:将比较器分配给常量将减少每次调用该方法所需的内存量,以换取较低的常量开销,这些开销将移至永久 GC 空间。
  • 引用逃逸的风险:内部类包含指向构造它的类的指针。如果内部类 (Comparator) 从创建它的方法中返回,那么对父对象的强引用可能会逃逸并阻止父对象的 GC。纯粹是一个可以潜入代码的陷阱,在这个例子中不是问题。

Hotspot 非常擅长内联,但不太可能认识到比较器可以在堆上分配或移动到常量。但这将取决于 TreeSet 的内容。如果 TreeSet 的实现非常简单(而且很小),那么它可以内联,但我们都知道事实并非如此。 TreeSet 也被编码为通用的,如果它只与一种类型的对象(Worker)一起使用,那么 JVM 可以应用一些优化,但是我们应该假设 TreeSet 也会被其他类型使用,因此 TreeSet 不会能够对传递给它的比较器做出任何假设。

因此,两个版本之间的区别主要在于对象分配。使用 final 关键字不太可能提高性能,因为 Hotspot 大多数情况下都会忽略 final 关键字。

Java 8 在使用 lambda 时有一个非常有趣的行为。考虑示例的以下变体:

import java.util.*;

public class T {
public void iterate(List<String> workers) {
SortedSet<Double> set = new TreeSet<>( Double::compare );
}
}

运行'javap -c T.class',你会看到如下jvm代码:

  public void iterate(java.util.List<java.lang.String>);
Code:
0: new #2 // class java/util/TreeSet
3: dup
4: invokedynamic #3, 0 // InvokeDynamic #0:compare:()Ljava/util/Comparator;
9: invokespecial #4 // Method java/util/TreeSet."<init>":(Ljava/util/Comparator;)V
12: astore_2
13: return

这里要注意的很酷的事情是 lambda 没有对象构造。 invokedynamic 在第一次被调用时会有更高的成本,然后它被有效地缓存。

关于java - 提取到 static final 是 Java 优化所必需的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33009338/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com