Java Akka http scala服务中的之字形堆内存模式

Java Akka http scala服务中的之字形堆内存模式,java,scala,garbage-collection,akka,newrelic,Java,Scala,Garbage Collection,Akka,Newrelic,我有一个基于AKKA-HTTP的服务,它是用scala编写的。此服务用作API调用的代理。它创建一个主机连接池,用于使用 该服务与NewRelic集成,并具有附加的快照 我想了解这种锯齿形模式的原因,即使服务上没有流量,并且主机池中的连接由于空闲超时而终止 此外,我还想知道,完整GC是否只有在达到阈值(比如7GB)后才会发生?或者它也可能发生在没有交通的其他时间 该服务具有8GB的XmX。此外,还有多个dispatcher(fork-join-executor)执行多个任务。一般来说,对于不

我有一个基于AKKA-HTTP的服务,它是用scala编写的。此服务用作API调用的代理。它创建一个主机连接池,用于使用

该服务与NewRelic集成,并具有附加的快照

我想了解这种锯齿形模式的原因,即使服务上没有流量,并且主机池中的连接由于空闲超时而终止

此外,我还想知道,完整GC是否只有在达到阈值(比如7GB)后才会发生?或者它也可能发生在没有交通的其他时间


该服务具有8GB的XmX。此外,还有多个dispatcher(fork-join-executor)执行多个任务。

一般来说,对于不进行引用计数的GC,您会看到这种锯齿形模式,因为只有在GC运行时才会回收内存

G1通常只收集堆中希望找到大量与活动对象相关的垃圾的区域(“垃圾收集”有点用词不当:它实际上包括收集活动对象和(在像G1这样重新定位垃圾收集器的情况下)将活动对象移动到堆的另一个区域,这样就可以声明它收集到的区域已准备好进行新的分配;因此,它需要处理的活动对象越少,相对于释放的内存,它需要做的工作就越少)

在高层次上,G1通过定义一个Eden(年轻一代)来工作,其中新创建的对象被分配到新创建的对象,它将Eden划分为多个区域,每个线程映射到一个区域。当一个区域填满时,只收集该区域,幸存者被转移到老一代(这很简单)。这会一直持续到幸存者世代满,此时将收集幸存者和伊甸园世代,并将幸存的幸存者提升到旧世代,当旧世代满时,您将拥有完整的GC

因此,触发完整GC不一定有一个固定的阈值,但一般来说,堆使用得越多,运行完整GC的可能性就越大。除此之外,JVM上的垃圾收集器往往或多或少是自治的:大多数会忽略
System.gc
和/或其他触发gc的尝试

可以想象,在G1中,如果您在启动时分配了一个多GiB数组,丢弃了引用,然后在每个空闲时间段重新分配了一个与启动时分配的数组大小相同的数组,然后丢弃了引用,那么您就有相当大的机会触发一个完整的GC。这是因为该数组足够大,可以绕过eden,直接进入旧一代,在下一代完整GC之前,它将使用堆。最终,旧一代中没有足够的连续可用空间来分配这些数组,这将触发完整的GC。这种方法的唯一复杂之处在于:

  • 您最终将不得不智胜JIT优化器,后者将看到您正在分配这个数组并将其丢弃,并决定它实际上不必分配这个数组

  • 如果自上次分配并丢弃后,您有足够长的忙时间运行完整GC,则无法保证在完整GC后分配大型阵列会成功,这将导致OOM


一般来说,对于不进行引用计数的GC,您会看到这种锯齿形模式,因为只有在GC运行时才会回收内存

G1通常只收集堆中希望找到大量与活动对象相关的垃圾的区域(“垃圾收集”有点用词不当:它实际上包括收集活动对象和(在像G1这样重新定位垃圾收集器的情况下)将活动对象移动到堆的另一个区域,这样就可以声明它收集到的区域已准备好进行新的分配;因此,它需要处理的活动对象越少,相对于释放的内存,它需要做的工作就越少)

在高层次上,G1通过定义一个Eden(年轻一代)来工作,其中新创建的对象被分配到新创建的对象,它将Eden划分为多个区域,每个线程映射到一个区域。当一个区域填满时,只收集该区域,幸存者被转移到老一代(这很简单)。这会一直持续到幸存者世代满,此时将收集幸存者和伊甸园世代,并将幸存的幸存者提升到旧世代,当旧世代满时,您将拥有完整的GC

因此,触发完整GC不一定有一个固定的阈值,但一般来说,堆使用得越多,运行完整GC的可能性就越大。除此之外,JVM上的垃圾收集器往往或多或少是自治的:大多数会忽略
System.gc
和/或其他触发gc的尝试

可以想象,在G1中,如果您在启动时分配了一个多GiB数组,丢弃了引用,然后在每个空闲时间段重新分配了一个与启动时分配的数组大小相同的数组,然后丢弃了引用,那么您就有相当大的机会触发一个完整的GC。这是因为该数组足够大,可以绕过eden,直接进入旧一代,在下一代完整GC之前,它将使用堆。最终,旧一代中没有足够的连续可用空间来分配这些数组,这将触发完整的GC。这种方法的唯一复杂之处在于:

  • 您最终将不得不智胜JIT优化器,后者将看到您正在分配这个数组并将其丢弃,并决定它实际上不必分配这个数组

  • 如果y