使用Java 11编译后出现堆泄漏

问题描述 投票:4回答:2

将我们的Spring Web App Maven构建升级到Java 11之后,我们发现Java进程的内存消耗不断增加。

工作正常:使用Java 8 JDK构建+在使用Java 11的服务器上运行

有泄漏:使用Java 11构建+在使用Java 11的服务器上运行

该泄漏在堆转储甚至本机内存跟踪中都不可见,该过程一直在增加,直到物理内存+交换已满并且该进程被系统杀死为止。什么样的问题甚至可能引起这种问题?

java memory memory-leaks heap java-11
2个回答
1
投票

在Java 11中,ForkJoinPool类的行为略有不同。

自终止线程以来,自上次使用以来的默认经过时间为60秒。在Java 8中,这没有记录,但实际上需要2秒钟进行硬编码。如果池过大,则Java 8实现会在创建池后两秒钟终止空闲线程。但是该类的Java 9/11版本使它们存活了几分钟。

比较线程的数量和生存期。由于启动应用程序或创建ForkJoinPools对象时,未使用的线程可能不会再提前终止,因此线程的生存期很容易导致内存问题。

有关类似问题,请参见以下问题:ForkJoinPool performance Java 8 vs 11

在Java 9中,引入了new constructor来配置该值。要获得与Java 8编译相同的行为,必须在编译为Java 9之前将keepAliveTime显式设置为2秒,或减小ForkJoinPool对象的大小。


0
投票

[深入研究Java的编译器代码后,我发现了一个有趣的变化,它是我尚未意识到的Java 9中引入的。根据您的编译目标,此更改可能导致不同的行为:

尽管众所周知,大多数优化是由JIT编译器而不是javac完成的,但后者仍会进行一些代码优化。在Java 9之前,这些优化之一是将String串联转换为StringBuilder :: append链。从Java 9开始,javac使用对新引入的java.lang.invoke.StringConcatFactory类的invokedynamic调用,而不是转换为StringBuilder:append调用。因此,当您编译为Java 8时,javac会生成优化的字节码,而当您编译为Java 9时,则在运行时将优化委托给上述内置类。

相应的JEP 280提供了有关此更改的更多详细信息。 JEP 280的一项成功指标是String的串联性能一定不能降低。但是JDK-8221760已经报告了潜在的性能下降。根据该错误条目,在Java 11u上,具有字符串连接编译为Java 8的代码似乎比在Java 9或11上编译的同一代码表现更好。该错误条目仍未解决,因此,性能并不是这里唯一的回归。

© www.soinside.com 2019 - 2024. All rights reserved.