如何禁用Java垃圾收集器?

40
我们有一个PHP Web应用程序,调用Java二进制文件生成PDF报告(使用JasperReports)。Java二进制文件将PDF输出到标准输出并退出;PHP然后将PDF发送到浏览器。这个Java命令大约需要3到6秒钟,当它持续6秒钟时,我认为是因为GC启动了。我想禁用GC,因为不管怎样,当命令退出时会释放所有内存。
我想知道如何在Java 1.4.2和Java 1.6.0中禁用它,因为我们目前正在测试两个JVM,以确定哪个执行速度更快。

7
给你一个提示,另一种方法(因为启动过程也需要时间):http://github.com/hudora/pyJasper 包含一个(Java)服务器,可以在请求时运行jasperreports,而无需进行进程启动/关闭的额外开销。 - ZeissS
与特定对象相关:https://dev59.com/CHM_5IYBdhLWcg3wjj4p - Ciro Santilli OurBigBook.com
10个回答

39

听起来你想要节省时间,但方法不对。禁用垃圾回收所能节省的时间(对于单个任务而言)微不足道,相较于启动和关闭Java进程所需的时间。如果运行时间性能是你的目标,你可以考虑创建一个Java进程,多次请求它执行你需要的工作。


是的,这是一个不错的方法,虽然还有更多的工作要做,我需要编写一种守护进程来监听套接字等等...除此之外,这个“守护进程”需要在X天后重新启动...因为从我的经验来看,Java会在长期运行中泄漏内存。 - Nelson
@Nelson,考虑使用类似Mule这样的工具来托管您的Java进程,并让它处理所有通信。其想法是您只需要将JasperReports嵌入到ESB可以托管并根据需要路由请求的服务中即可。Mule: http://www.mulesoft.com/mule-esb-open-source-esb - Trevor Tippins
4
Java 并非本质上会泄漏内存。我们的 Java 守护程序运行了数月之久,仅在应用程序更新或系统停机时重新启动。如果您的 Java 程序存在内存泄漏问题,请尝试使用分析器。还有其他选项可供选择,而不必自己打开套接字。可以使用 ActiveMQ/JMS、JMX 或者创建一个 Web 服务,也许 jetty 是一个有趣的选择。 - Hardcoded

33

无法完全禁用垃圾回收。当JVM耗尽空间时才会运行垃圾回收,因此您可以为程序分配更多内存。将以下命令行选项添加到Java命令中:

-Xmx256M -Xms256M

这将为程序提供256MB的内存(默认值为64MB)。然而,对于默认大小的JVM,垃圾收集不会花费3秒钟的时间,因此您可能需要更仔细地调查程序正在执行什么操作。Yourkit profiler非常有用,可以帮助找出哪些操作需要较长时间。


32

只有在JVM内存短缺时,垃圾回收才会触发,所以你要么进行垃圾回收,要么就会程序崩溃。尝试开启详细的垃圾回收并查看是否实际花费了大量时间。

java -verbose:gc

1
哦,那也许我可以使用命令行开关来为其分配更多内存,从而增加GC不触发的几率。 - Nelson
2
是的,在分析verbose:gc的输出后,您将知道是否需要这样做。那是第一步。 - unbeli
8
+1 表示支持“测试,不猜测”。时间差可能是完全无关的,比如I/O争用或某些耗费大量CPU资源的进程启动。 - gustafc

18

Java 11提供了一个无操作垃圾回收器

您可以在JVM启动时通过-XX:+UseEpsilonGC选项启用它。

根据JEP说明书,其中一个目标是使某些短暂的作业更加高效,这可能是您的使用场景:

非常短暂的作业。短暂的作业可能依赖于快速退出以释放资源(例如堆内存)。在这种情况下,接受徒劳地清理堆的GC循环是一种浪费时间的行为,因为堆最终会在退出时被释放。请注意,GC周期可能需要一段时间,因为它取决于堆中活动数据的数量,这可能很多。


我可以想到其他重要的用例:https://www.youtube.com/watch?v=zQVytExlnEk - earizon

6
Java 11提供了二进制选项,可以选择开启或关闭Java GC。要关闭Java GC,您需要使用Epsilon垃圾收集器,在命令行上将其关闭。在Java 11上,使用以下两个JVM参数:
-XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC

没有UnlockExperimentalVMOptions参数,JVM将无法启动,请确保它已包含。

无法停止Java GC

不幸的是,如果您没有使用Epsilon GC,就没有办法禁用、停止或防止垃圾回收发生。就像不能触发GC一样,您也不能停止Java GC。算法是非确定性的,只有JVM可以控制它们何时发生。


2

您可以使用-Xmx选项来设置最大堆大小;使用更大的堆应该可以防止虚拟机过早地耗尽内存并需要进行垃圾收集。


2
仅仅设置-Xmx是没有任何作用的。实际上,你还需要设置-Xms,因为你想要控制初始堆大小。 - Stephen C

1

你确定是垃圾回收导致了减速吗?你是否使用-java参数并加上-verbose:gc来查看发生了什么?

JVM上无法禁用垃圾回收。但你可以考虑tuning垃圾回收器以获得更好的性能。


0

与其他人所说的相反,有一种悬挂垃圾收集器的方法,虽然非常复杂。

如果通过JNI调用本地函数,在本地代码调用GetPrimitiveArrayCritical和ReleasePrimitiveArrayCritical之间,GC将被暂停。这是因为它是用于在Java和本地代码之间共享内存的机制,而GC可能会移动内存中的对象,必须这样做。

因此,要利用此功能,您需要创建一个JNI函数,该函数调用前者,然后轮询写入该临界数组的标志(它可以只是一个byte[1]),等待看到该标志,然后调用后者。当Java代码希望暂停GC时,它将调用JNI方法,当它希望恢复GC时,设置上述标志(我相信对关键数组的读/写是易失性的,因此本地代码将立即看到该标志)。

现在我并不是说这是一个好主意,当然也不是解决OP问题的正确方法。但是,如果您绝对必须暂时暂停GC以某种原因(也许您希望通过sun.misc.Unsafe操纵原始内存,并需要确保在此期间GC不移动对象),那么就可以实现它。


-2

正如大家所说,你不能在JVM中禁用GC,这是有道理的,因为如果可以禁用GC,由于Java没有显式的方式让开发人员删除堆数据,就会导致内存泄漏。

你是否可以访问此Java二进制文件的源代码?如果可以,分析它并查看是否有任何瓶颈可以更好地编写以减少GC活动。这可以使用大多数Java分析器(例如JProbe)完成。


Java二进制文件非常简单,它只是检索命令行参数并调用Jasperreport函数来创建报告,然后将报告传递给System.out输出流,在那里PHP通过'passthrough'函数读取它。因此,时间花费在Jasperreport处理报告上,这就是我想要Java通用加速的原因。 - Nelson
看起来控制JVM GC设置是你唯一的选择。 - display101

-4
为了避免垃圾回收器释放任何对象的变量或属性,您必须在类中将此属性(由gc释放)设置为静态的。这是我的解决方案。
例如: private static String myProperty;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接