了解JVM内存优化

时间:2017-10-13 10:31:30

标签: java memory-management

我最近接受了在Java服务器中对内存利用率进行基准测试和优化的任务,同时修复了内存泄漏问题。我们有4台JVM服务器在一台机器上运行,它有15G物理内存和32G交换内存。以下是free -m快照。

              total        used        free         shared  buff/cache    available
Mem:          15289       14786         392           1         110         342
Swap:         32767        3776       28991

如果我理解正确: -

  1. 大多数物理内存正在使用中,大部分交换内存都是免费的。
  2. 实际上缓冲了少量的内存。
  3. 我有48GB的虚拟内存
  4. 现在我的每台服务器都运行7G最大堆大小设置为-Xmx选项。以下是GC Utils的输出

    $ jstat -gcutil 8317
      S0     S1     E      O      M     CCS      YGC     YGCT    FGC    FGCT     GCT
      0.00   0.00   6.97   0.22  96.77  91.89     72    8.027    33   10.975   19.002
    $ jstat -gcutil 8332
      S0     S1     E      O      M     CCS      YGC     YGCT    FGC    FGCT     GCT
      0.00   0.00   5.17  50.76  96.57  91.65    274   51.001    51  179.106  230.107
    $ jstat -gcutil 8249
      S0     S1     E      O      M     CCS      YGC     YGCT    FGC    FGCT     GCT
      0.00   2.10  80.37  69.53  96.87  92.08    421   69.478    13   56.569  126.047
    $ jstat -gcutil 23875
      S0     S1     E      O      M     CCS      YGC     YGCT    FGC    FGCT     GCT
     27.91   0.00  37.07  86.29  97.59  94.60    232    7.294     2    0.030    7.324
    

    运行手动Full GC后,它完全释放了所有服务器的年轻一代。现在我需要帮助来理解这个特征,基本上我在假设下面是正确的吗?

    1. 物体的移动取决于寿命和幸存数量为Eden - > S0 - > S1-> O操作。
    2. 其中三台服务器拥有Old Gen> Young Gen.这是否意味着Full GC运行不足?
    3. 或者,我需要调整SurvivorRatio和NewRatio等设置,让Young Generation有更多机会收集物品,并延迟将物体移动到旧空间。
    4. 然而,增加Y空间意味着较短的GC和较差的应用程序吞吐量,我是否应该增加内存并且不再担心调整?
    5. 我问这个,因为Full GC确实释放了所有内存,因此没有内存泄漏但是如何通过调整GC设置在内存使用和应用程序吞吐量之间取得平衡。任何人都可以通过考虑上述机器状态来提供更多的指导或推荐吗?

      JVM Details:
      Java Version: openjdk version "1.8.0_131" Server Class
      GC: Parallel Collector(Default one)
      

1 个答案:

答案 0 :(得分:2)

简而言之:如果你想了解你的系统,基本上在某个时间点进行内存使用是一个坏主意。随着时间的推移,查看行为会更有帮助。为此,请使用JConsole或专业分析器(如YourKit或JProfiler)。另一方面,Hotspot-VM的默认值是非常合理的,所以除非你有一个非常具体的应用程序内存配置文件,你可能不会通过简单地在问题上投入更多硬件来摆弄并赢得更多。

请注意,您的服务器很可能尺寸不足(请参阅问题4)

答案很长:

问题1: 对象如何通过不同的内存区域?每个对象都是在eden空间中创建的。第一个小型垃圾收集(也会触发一个主要收集)将把该对象放入幸存者(如果有足够的空间)或直接进入终身。对于已经存在于幸存者中的对象,它们从一个幸存者空间移动到另一个幸存者空间,并且在某个时刻(由VM内的启发式确定)被移动到终身。规则总是成立,至少有一名幸存者总是空着。

问题2: 对年龄大于年轻人的占有意味着,你不经常收集吗?绝对不是。如果很少执行,Java中的垃圾收集效果会更好(效率更高)。基本上,如果自上次收集以来死亡的对象越来越多,则收集效率会更高,因为它可以立即清理更多内存。 gc的设计目的是在触发full-gc之前让终端填满,直到达到非常高的填充水平(接近100%)。我们观察到生产系统几周没有完成全部gc,因为年轻一代足够大,以至于所有临时分配都在伊甸园或幸存者中死亡。除此之外:所有的缓存,Spring Beans或其他任何东西都将永久保留在终身。

问题3: 我应该调整内存参数以优化内存消耗吗?一定要:不!除非你确切地知道你正在做什么,否则永远不要碰那些东西。扩大年轻一代可能会导致您的应用程序死于内存不足错误。在摆弄这些东西之前,您应该非常清楚应用程序的长期行为,并且热点VM的启发式方法非常好。另一方面,如果你知道,你正在做什么,它可以显着减少gc次数。在一个系统(上面提到的系统)中,我们看到次要集合从一次~20秒减少到几分钟一次,并且在调整后几乎完全停止在全gcs中。但是在学习和理解的过程中,我们的应用程序如何随着时间的推移在内存方面表现得很好,我们也杀了很多命令。

问题4: 我应该增加内存而不是摆弄?取决于。您的系统不健康。增加记忆会使情况变得更糟。在具有16g物理内存的系统上运行4个VM,堆最多7g(总共28g)。如果你的所有虚拟机都将内存消耗扩大到最大值,你会看到很多交换,这肯定会将性能降低到你难以想象的水平。我会在场景中寻找更多的硬件,特别是考虑到,java应用程序的实际消耗大约是堆的1.5倍(你必须添加permgen / metaspace,堆栈,codecache和vm本身消耗的内存) )。如果您确定知道,您的应用程序正在运行-xmx7g,您可以从那里开始:增加整体内存,同时减少旧的部分,以便绝对旧的大小保持不变。然而,这意味着更多的硬件。