java - HotSpot 中 Runnable 的成本

标签 java lambda jvm runnable jvm-hotspot

每当我用Java编写异步代码时,我都必须使用Runnable(FunctionCallable,...)或新的 lambda 语法。与 C++ 模板不同,无法保证它会被编译器内联。

在什么情况下编译器可以对其进行优化,这意味着比实例化 Runnable 对象更有效? JIT 怎么样?例如,流操作、惰性初始化、回调?

如果不进行优化,HotSpot 是否可以管理数百万个 Runnable 实例,而不会给 GC 带来任何重大开销?一般来说,我是否应该担心应用程序中 lambda 和回调的广泛使用?

最佳答案

首先,您需要了解 javac 编译器的作用以及 JVM 通过 JIT 编译器执行的操作。

Runnable 是一个接口(interface),因此您可以创建一个实现该接口(interface)的类,然后将其实例传递给 Thread 构造函数,也可以使用匿名内部类 (AIC)。在这种情况下,javac 编译器将为您生成一个实现 Runnable 的合成类并为您创建一个实例。

C++ 使用静态提前 (AOT) 编译,并且正如您所说,可以内联模板。 JVM 使用自适应即时 (JIT) 编译。当加载类文件时,字节码将被解释,直到 JVM 确定代码中存在热点并将它们编译为可以缓存的 native 指令。使用的优化程度取决于所使用的 JIT。 OpenJDK 有两个 JIT:C1 和 C2(有时称为客户端和服务器)。 C1 编译代码更快,但优化更少。 C2 的编译时间更长,但优化更多。如果编译器认为这是最佳优化,则 Runnablerun() 方法将被内联(这意味着如果它被大量使用,它很可能会被内联)。我们 Azul(我为他们工作)最近发布了一个新的 JVM JIT,称为 Falcon,基于 LLVM,它进一步优化。

Lambda 有点不同。任何 Lambda 表达式都可以转换为等效的 AIC,对于 JDK 8 中的早期实现,这就是它们的实现方式,作为 AIC 的语法糖。为了优化性能,javac 现在生成使用 invokedynamic 字节码的代码。通过这样做,它将 Lambda 的实现方式留给了 JVM,而不是将其硬编码在类文件中。 JVM可以使用AIC,它可以使用静态方法或一些其他实现方法。值得注意的是,使用方法引用而不是显式 Lambda 的性能稍好一些。

对于您问题的 GC 方面,这取决于您的代码的配置文件。如果您使用数百万个 Runnable 对象,我会更关心 Thread 对象的影响。如果您不池化这些线程,那么创建和收集数百万个线程的 GC 开销将远远超过 Runnable 对象。只要可以在 Eden 空间中收集 Runnable 对象,开销实际上为零。

关于java - HotSpot 中 Runnable 的成本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46141493/

相关文章:

c# - 创建返回排序 lambda 表达式的通用方法

c++ - 'Chunk * _chunk = new (size) Chunk(size)' 是什么意思?

java - 有没有一种简单的方法来获取Java中特定类的所有对象实例

java - 单元格的权重向右而不是向左

java - 如果元素可见执行操作并通过测试,否则仅通过测试

c++ - 如何给 lambda 一个持续时间与 lambda 一样长的内部值?

java.lang.NoSuchFieldError : DEF_CONTENT_CHARSET 错误

java - 提取两个用空格填充的管道之间的数字

java - setOnItemClickListener() 不适用于 Fragment 中的 ExpandableHeightGridView

c# - 为动态对象创建 linq 表达式树