假设我们有三个这样的方法。它们都做同样的事情,但它们在内存分配和效率方面有何不同?方法一会在每次调用期间创建 Function 的实例,但第二个方法会这样做吗?应该始终使用第三个版本还是其中一个版本是安全的并且 JIT 编译器会负责内存优化?
class Test {
Map<String, Set<String>> mapOfSets = new HashMap<>();
static final Function<String, Set<String>> FUNCTION = s -> new HashSet<>();
void method1(String key, String value) {
Function<String, Set<String>> func = s -> new HashSet<>();
mapOfSets.computeIfAbsent(key, func).add(value);
}
void method2(String key, String value) {
mapOfSets.computeIfAbsent(key, s -> new HashSet<>()).add(value);
}
void method3(String key, String value) {
mapOfSets.computeIfAbsent(key, FUNCTION).add(value);
}
}
Run Code Online (Sandbox Code Playgroud)
有两种方法可以回答这个问题:
描述 lambda 表达式求值的 Java 语言规范部分旨在为编译器/实现提供是否创建新对象的灵活性。您的问题归结为 lambda 对象是否在代码的三个版本中被重用。根据 JLS 的说法,答案取决于实现。100% 确定的唯一方法是转储 JIT 编译器1生成的本机代码。然而,根据 @Holger 对其他问题的回答2,当前基于 OpenJDK 的 Java 运行时确实会缓存和重用非捕获 lambda。
鉴于行为和性能将取决于 Java 实现,因此是否应该选择一种形式而不是另一种形式是有争议的:
这是一个见仁见智的问题,但我认为在大多数情况下,问题 1 和问题 2 的答案都是“否”。将其留给 JIT 编译器来处理优化,除非分析表明优化对应用程序的整体性能非常重要,并且应用程序的整体性能确实很重要。
1 - 请参阅如何在 JVM 中查看 JIT 编译的代码?。
2 - 请参阅/sf/answers/1926718041/和/sf/answers/1679393761/。
正如评论所提到的,这可能会根据您使用的 Java 实现而改变。
对于热点 jvm(普通/默认 java 实现),您可以期望所有 3 个选项在性能方面都相同。起初您可能认为 3 可能更好,因为 1 和 2 可能需要在每次调用时创建一个新的 Function 对象。但是,您每次都需要为 method1(和 2)创建一个新对象的假设是错误的,因为非捕获 lambda 将缓存在调用站点,因此每次调用都将重用相同的实例,就像使用 method3 一样你不会看到任何区别。
这个答案是,并且对该主题的出色概述更详细
因此,在这种情况下,您可以毫无顾虑地使用这三个中的任何一个,因此请使用最适合您情况的那个。
| 归档时间: |
|
| 查看次数: |
1484 次 |
| 最近记录: |