来自RedHat的性能和OpenJDK开发者Aleksey Shipilëv,提交了一份新的JEP草案,其内容为创建一个无操作垃圾回收器:一种实际上不进行实际内存回收的GC方式。该回收器旨在帮助JVM实现者和研究者,以及少部分无需垃圾回收的超高性能应用程序。如果这项JEP继续推进,新的GC方式将会和现有GC方式一起存在,并且通过显式激活方式使用。
\\垃圾回收和Java性能向来都是复杂的话题,为了能够更清晰的说明,InfoQ联系了Java Champions成员、性能专家Martijn Verburg和Kirk Pepperdine。同时,我们也联系了无GC Log4j转型领导者Remko Popma,作为如何实现这一GC方式的代表人物之一。Martijn和Remko确认,在他们看来,无操作GC——既Epsilon GC的首要受益者是GC的开发者和性能研究人员。Epsilon GC可以作为度量其他垃圾回收器性能的对照组。一个简单的例子,我们可以将一个应用程序的GC方式设置成无操作GC以减少GC开销(以避免内存分配和其他不可控因素)。如果相同应用程序在相同情况下运行,只是修改了不同GC算法配置,性能上的不同就可以证明该种GC方式在性能上的开销。这将会帮助GC开发者和性能研究人员在更加孤立的方式理解垃圾回收行为。
\\\\\“我认为这实际上是对精确度量JVM各个部分跨出的一大步。(例如现存JIT的C1/C2编译器,可能会被转移到Graal等)。它将会为JVM增加额外的寿命。”——Martijn Verburg
\
另一方面,一些对性能严格要求的应用程序也将会从Epsilon GC受益。有非常少见的应用程序和库,例如前文提到的Log4j,已经被设计成不产生垃圾,因此他们无需使用垃圾回收器。对于这种类型应用程序,移除垃圾回收器可以提升性能。然而,如Remko强调的,构建一个可以运行在Epsilon GC的库,“将花费大量的工程努力,以确保应用程序的内存被自己管理,不会耗尽”。但是这样也必须进行风险和性能评估,以确认选择无操作GC的收益和实现无垃圾状态做的努力是否相同。
\\应用程序如何可以不产生垃圾看上去很难想象,而且这个话题已经复杂到超出本文讨论的范畴,但是如果考虑以下几个方面可能会容易理解:
\\- JVM将内存分成两个部分来管理:堆和栈。这就是为什么当缺少内存时会有两个不同的错误(OutOfMemoryError和StackOverflowError)。栈内存只能被当前线程和当前执行的方法访问,因此,当线程离开当前执行的方法,这块内存会被自动释放,而无需额外垃圾回收器。然而,堆内存可以被整个应用程序在任何时刻访问,这意味着独立的垃圾回收器需要区分内存块什么时候才不被使用,可以被回收。\\t
- 基本数据类型内存总是在栈上分配,因此这对垃圾回收器没有压力。如果代码中基本上都使用基本类型,那么垃圾回收器处理的对象就少了。\\t
- 不产生垃圾不等于不创建对象,如果对象创建满足以下几个条件,仍然可以在创建对象之后不需要垃圾回收器:\\t
- 应用程序或者库在初始化的时候生成有限个数的对象,然后不断复用这些对象。但是这需要依赖开发者非常熟悉应用程序的内存占用。\\t\t
- 有的时候编译器可以发现一些特定对象不会在方法外使用,这被称为逃逸分析。当确认对象生命周期不会超过方法,其内存可以分配到栈而非堆。因此,这些对象占用的内存会在当前方法结束的时候自动消除。\\t
虽然去除垃圾回收可能实现,Kirk指出这样编写代码会非常不自然,并且会失去Java提供的很多特性。Martijn也同时指出,内存管理恰恰也是Java能够在业界成功的一个主要原因。另外,我们需要牢记,虽然被叫做垃圾回收器,垃圾回收器的任务不仅仅是回收无用的内存,还包括分配新的内存块,Epsilon GC仍然需要实现该功能。这也正是Kirk的论据,至少理论上,使用Epsilon GC和将其他垃圾回收算法配置成实际不进行垃圾回收之后,在性能上没有显著差异。
\\然而,尽管考虑到所有这些注意事项,Kirk和Martijn证实Epsilon GC还是会对一小部分受众非常有用,但是根据Kirk所说,这些受众将非常少,并怀疑它的实际用处。绝大部分应用程序需要在一些时刻回收内存,因此需要一个有实际功能的垃圾回收器。
\\\\\“合理的GC停顿时间对于大部分应用程序来说不是问题,因此为什么要为了一个可能存疑的性能优势而放弃Java的所有好处?”——Kirk Pepperdine
\
Kirk同时提到,新增的每一项新特性都会增加OpenJDK的维护成本,因此OpenJDK开发人员在添加新特性时必须有全局考虑。Oracle一直在减少垃圾回收器的数量,以降低维护成本,因此为一小部分用户添加一个垃圾回收器可能不是一个合理的投资。Aleksey在起草JEP草案的时候,看上去考虑到了这些点,通过JEP草案内容和已经提供的原型来看,初步分析开销可能不大。事实上,Kirk和Martijn同时指出,考虑到Aleksey的经验,应该对他所领导的这个倡议保持乐观。
\\另一方面,Kirk也强调,虽然OpenJDK是JVM的一个参考实现,但是对垃圾回收器没有强制性要求,这意味着只要完全兼容Java规范,分发者可以有他们自己的垃圾回收算法实现。这可能导致公众意见的分歧:其中一部分人可能认为为特定市场实现的算法可能更适合商业版本的JVM,另一些人可能会认为这对OpenJDK是一个非常有用的补充。
\\Remko还建议当性能变得至关重要时,应该考虑使用商业版JVM,因为在购买许可证上的开销会比让工程师选择和调优特定的GC算法要小。但是,即使有些人只选择使用OpenJDK,Remko和Martijn都提到当前正在开发中的Shenandoah GC,其目的是针对非常大的堆内存(100GB甚至更大)也只有超低停顿时间。无论如何,专家们的共识是,当应用程序遇到性能问题时,一个经过谨慎选择的GC算法总是优于完全没有GC。
\\该提案仍然处于初级阶段,在称为正式JEP之前仍需要进行审查和润色。让它称为正式稿之后,将会被加入到JVM版本中。虽然目前我们只能推测最终会添加到什么版本JVM中,Martijn认为有理由期待Epsilon GC将会加入到Java 10或者11。如果要说有什么好处的话,Epsilon GC至少能够帮助理解GC的接口,有助于成就一个更加模块化的JVM。
\\查看英文原文:The Last Frontier in Java Performance: Remove the Garbage Collector