【发布时间】:2020-06-03 21:22:24
【问题描述】:
我们有非常频繁调用的 Java Stream API 方法,例如每秒 10'000 - 20'000 次(数据流系统)。让我们回顾一下以下简单的test 方法(故意简化,并没有真正的价值):
public void test() {
Stream.of(1, 2, 3, 4, 5)
.map(i -> i * i)
.filter(new SuperPredicate())
.sorted(Comparator.comparing(i -> -i + 1, Comparator.nullsFirst(Comparator.naturalOrder())))
.forEach(System.out::println);
}
class SuperPredicate implements Predicate<Integer> {
public SuperPredicate() {
System.out.println("SuperPredicate constructor");
}
@Override
public boolean test(Integer i) {
return i % 3 != 0;
}
}
在每次调用test 方法时,都会创建功能接口的新实例(在我们的示例中为SuperPredicate 和Comparator.nullsFirst())。所以对于频繁的方法调用,会创建数以千计的多余对象。我知道在 Java 中创建对象需要几纳秒,但如果我们谈论高负载,它也可能会增加 GC 的负载,从而影响性能。
正如我所见,我们可以将此类函数式接口的创建移至同一类中的private static final 变量中,因为它们是无状态的,因此会稍微减少系统负载。这是一种微优化。我们需要这样做吗? Java 编译器/JIT 编译器是否以某种方式优化了这种情况?或者编译器可能有一些选项/优化标志来改善这种情况?
【问题讨论】:
-
您过滤和排序,但担心这个?是时候学习使用分析器了!
-
如果您有一个良好的性能测试环境,启用 JIT 编译日志可能会很有启发性。 (-XX:+UnlockExperimentalVMOptions, -XX:+LogCompilation)
-
保存你的 SuperPredicate 对象可能比每次都调用构造函数更好,但这是一个很小的优化,它可能无关紧要。正如 Thorbjørn Ravn Andersen 所说,排序和过滤将使这些微小的成本黯然失色
-
如果性能真的那么重要,我会考虑完全原始,使用数组和 int:s
-
@VasiliySarzhynskyi 不要相信我说的话 - 自己衡量。
标签: java optimization java-stream