为什么运行Java应用程序的机器几乎耗尽了物理内存,但仍能运行数周

为什么运行Java应用程序的机器几乎耗尽了物理内存,但仍能运行数周,java,performance,Java,Performance,我有一个Java应用程序,它部署在两台机器上,根据指标(包括JMC)判断,它们都几乎没有内存,并且持续了相当长的时间。但是,应用程序运行正常,不会发生OutOfMemory错误。 这令人困惑。你能解释一下为什么应用程序在没有OutOfMemory错误的情况下仍然运行吗?为什么内存没有比它如此接近极限时更早释放 PS这里是堆度量 Java的内存管理就是这样工作的——它基本上会将垃圾收集延迟到必要时,希望应用程序在需要GC之前退出 因此,它通常会选择从操作系统分配更多内存,直到-Xmx或OS/to

我有一个Java应用程序,它部署在两台机器上,根据指标(包括JMC)判断,它们都几乎没有内存,并且持续了相当长的时间。但是,应用程序运行正常,不会发生OutOfMemory错误。

这令人困惑。你能解释一下为什么应用程序在没有OutOfMemory错误的情况下仍然运行吗?为什么内存没有比它如此接近极限时更早释放

PS这里是堆度量


Java的内存管理就是这样工作的——它基本上会将垃圾收集延迟到必要时,希望应用程序在需要GC之前退出

因此,它通常会选择从操作系统分配更多内存,直到
-Xmx
或OS/total RAM limit给出一个限制,然后才执行主要的垃圾收集器运行。这些运行将释放一些内存,然后应用程序将再运行一段时间,直到再次遇到限制

如果应用程序逐渐需要更多的内存,那么在达到内存限制后,主要GC运行将变得越来越频繁(随着所需内存的增加,GC清除的内存减少),GC运行将变得更长。在某个时刻,VM在GC中花费的时间可能比运行应用程序花费的时间更多

这样,Java进程可能会运行很长时间(可能是“永远”)并消耗几乎所有可用内存,而实际应用程序几乎没有实际运行时间,因为几乎所有的CPU时间都花费在GC中。Java可能会检测到这种情况并抛出
Java.lang.OutOfMemoryError
,其中消息
GC开销限制超过了
,但并不总是这样


如果你想诊断你的应用程序是否只是在优化内存使用,或者实际上是内存不足,你需要了解VM在GC中花费了多少时间,以及趋势是什么。一些可能的方法:

  • 连接JConsole或VisualVM(两者都是JDK的一部分)并检查GC运行(间隔和持续时间)
  • 如果Java应用程序是单线程的,一种更简单的方法是观察CPU负载-默认GC是多线程的,因此在GC运行期间,它将主要在更多处理器/内核上运行,CPU负载将超过单个内核的100%(>100%在unix
    top
    ),而单线程应用程序只会导致单个内核的负载高达100%

您的应用程序在做什么?如果它一直在处理大量数据,那么它将一直使用大量内存。你在开放资源吗?您是否正确地关闭了它们?JVM分配内存的配置是什么?如果它比正常值更接近您在这里看到的值,因为GC在您不使用它之后不会清除内存-这是在经过一段时间。特别是当您处理大量数据时。@Lino这是一个后端,用于存储百万用户的数据。它缓存、映射数据并将请求转发给其他服务。我不完全清楚它打开的资源。换句话说,如果情况危急,我早就没有内存了,否则我的应用程序会执行得很慢?这一个与Java 8有关,永久性的生成限制删除。因此,永久生成将扩展到可用的物理内存,以保持应用程序运行,而不会发出OutOfMemoryError。所有的字符串和静态引用都保留在那里,到时候它可能会变得相当大。@kas-kad,这就是为什么我在Java8中说,它们删除了永久的生成限制。永久生成是堆外的一部分,现在没有明确的限制。通过-Xmx,您可以定义最大堆大小。请参阅本文件-.@kas-kad。不,即使你想,你也做不到。他们移除了永久的世代空间。我的错,我之前没提过。他们删除了Perm Gen空间,并创建了名为Metaspace的新内存空间。现在这个元空间位于本机内存中。如果不定义元空间大小,它将耗尽本机内存。为此,您可以使用
-XX:MaxMetaspaceSize=512m
开关。让我知道它是否有效。我来回答一下。延迟GC的一个更重要的原因是以后的GC通常可以收集更多的垃圾。