📜  Java虚拟机-调整GC

📅  最后修改于: 2020-11-13 05:38:21             🧑  作者: Mango


在上一章中,我们了解了各种Generation Gcs。在本章中,我们将讨论如何调整GC。

堆大小

堆大小是影响Java应用程序性能的重要因素。如果太小,它将经常被填充,结果,GC必须经常对其进行收集。另一方面,如果我们只是增加堆的大小,尽管需要减少收集频率,但是暂停的时间会增加。

此外,增加堆大小会对基础OS造成严重损失。使用分页,该操作系统使我们的应用程序看到的内存远远大于实际可用的内存。操作系统通过使用磁盘上的一些交换空间,将程序的非活动部分复制到磁盘中来进行管理。当需要这些部分时,操作系统会将它们从磁盘复制回内存。

让我们假设一台机器有8G的内存,而JVM看到了16G的虚拟内存,那么JVM不会知道实际上系统上只有8G可用。它只会从操作系统请求16G,一旦获得该内存,它将继续使用它。操作系统将必须交换大量数据进出,这将对系统造成巨大的性能损失。

然后是在这种虚拟内存的完整GC期间会出现的停顿。由于GC将作用于整个堆进行收集和压缩,因此它将不得不等待大量时间才能将虚拟内存从磁盘中换出。在使用并发收集器的情况下,后台线程将不得不等待大量时间,以便将数据从交换空间复制到内存。

因此,这里出现了如何确定最佳堆大小的问题。第一条规则是永远不要请求操作系统比实际内存更多的内存。这将完全避免频繁交换的问题。如果机器已安装并正在运行多个JVM,则所有这些JVM的总内存请求将小于系统中的实际RAM。

您可以使用两个标志来控制JVM的内存请求大小-

  • -XmsN-控制请求的初始内存。

  • -XmxN-控制可以请求的最大内存。

这两个标志的默认值取决于基础操作系统。例如,对于在MacOS上运行的64b JVM,-XmsN = 64M且-XmxN =至少1G或总物理内存的1/4。

请注意,JVM可以在两个值之间自动调整。例如,如果发现发生了太多的GC,则只要它在-XmxN以下并且满足所需的性能目标,它就会继续增加内存大小。

如果您确切知道应用程序需要多少内存,则可以设置-XmsN = -XmxN。在这种情况下,JVM不需要找出堆的“最佳”值,因此,GC流程变得更有效率。

代大小

您可以决定要分配给YG多少堆,以及要分配给OG多少堆。这两个值都通过以下方式影响我们的应用程序的性能。

如果YG的大小很大,则收集频率会降低。这将导致较少数量的对象被提升为OG。另一方面,如果太大增加OG的大小,则收集和压缩它会花费太多时间,这将导致较长的STW暂停。因此,用户必须在这两个值之间找到平衡。

以下是可用于设置这些值的标志-

  • -XX:NewRatio = N: YG与OG的比率(默认值= 2)

  • -XX:NewSize = N: YG的初始大小

  • -XX:MaxNewSize = N: YG的最大大小

  • -XmnN:使用此标志将NewSize和MaxNewSize设置为相同的值

YG的初始大小由NewRatio的值通过给定公式确定-

(total heap size) / (newRatio + 1)

由于newRatio的初始值为2,因此上述公式将YG的初始值为总堆大小的1/3。您始终可以通过使用NewSize标志显式指定YG的大小来覆盖此值。该标志没有任何默认值,如果未明确设置,则将使用上述公式继续计算YG的大小。

永久和元空间

永久和元空间是JVM保留类的元数据的堆区域。该空间在Java 7中被称为“永久空间”,在Java 8中被称为“元空间”。该信息由编译器和运行时使用。

您可以使用以下标志来控制永久磁铁的大小: -XX:PermSize = N-XX:MaxPermSize = N。可以使用-XX:Metaspace- Size = N-XX:MaxMetaspaceSize = N来控制元空间的大小。

当未设置标志值时,如何管理永久位和元空间存在一些差异。默认情况下,两者都具有默认的初始大小。但是,尽管元空间可以根据需要占用尽可能多的堆,但永久性最多只能占用默认的初始值。例如,64b JVM具有82M的堆空间作为最大永久大小。

请注意,除非另有说明,否则元空间可以占用无限量的内存,因此可能会出现内存不足错误。每当调整这些区域的大小时,就会发生完整的GC。因此,在启动过程中,如果要加载很多类,则元空间可以不断调整大小,从而每次生成完整的GC。因此,万一初始元空间大小太小,大型应用程序将花费大量时间来启动。增加初始大小是一个好主意,因为这样可以减少启动时间。

尽管永久性元数据和元空间保存了类元数据,但它不是永久性的,并且该空间像对象一样被GC回收。通常在服务器应用程序的情况下。每当您对服务器进行新的部署时,都必须清除旧的元数据,因为新的类加载器现在将需要空间。 GC释放了该空间。