Java 热点中可运行的成本

Java 热点中可运行的成本,java,lambda,jvm,runnable,jvm-hotspot,Java,Lambda,Jvm,Runnable,Jvm Hotspot,每当我用Java编写异步代码时,我都必须使用Runnable(Function,Callable,…)或新的lambda语法。没有保证它将被编译器内联,不像C++的模板。 在什么情况下,编译器可以对其进行优化,这意味着比实例化Runnable对象更有效?那么JIT呢?例如,流操作、惰性初始化、回调 如果未进行优化,HotSpot能否管理数百万个可运行的实例,而不对GC造成任何重大开销?一般来说,我是否应该担心在应用程序中大量使用lambda和回调?首先,您需要了解javac编译器的功能以及JVM

每当我用Java编写异步代码时,我都必须使用
Runnable
Function
Callable
,…)或新的lambda语法。没有保证它将被编译器内联,不像C++的模板。 在什么情况下,编译器可以对其进行优化,这意味着比实例化
Runnable
对象更有效?那么JIT呢?例如,流操作、惰性初始化、回调


如果未进行优化,HotSpot能否管理数百万个可运行的实例,而不对GC造成任何重大开销?一般来说,我是否应该担心在应用程序中大量使用lambda和回调?

首先,您需要了解javac编译器的功能以及JVM通过JIT编译器的功能

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

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

lambda有点不同。任何Lambda表达式都可以转换为等价的AIC,对于jdk8中的早期实现,这就是它们的实现方式,作为AIC的语法糖。为了优化性能,
javac
现在生成使用
invokedynamic
字节码的代码。通过这样做,它为JVM留下了实现Lambda的方式,而不是在类文件中对其进行硬编码。JVM可以使用AIC,也可以使用静态方法或其他实现方法。次要的一点是,使用方法引用而不是显式Lambda对于性能来说稍微好一点


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

您更有可能遇到其他性能问题,因此您不应该太担心它。异步代码应该如何内联?如果它是内联的,就不会是异步的。当然,这也适用于C++。执行者需要接收具有身份的东西,以知道要执行什么,通常是C++中的内存位置。因此,为异步作业的表示保留内存是不可避免的。唯一的例外是重复执行相同的不变操作,在这种情况下,Java的lambda表达式或方法引用也将对相同的对象求值。