【问题标题】:Tomcat memory consumption is more than heap + permgen spaceTomcat内存消耗大于heap + permgen空间
【发布时间】:2011-12-23 16:30:00
【问题描述】:

我观察到操作系统所说的和 jVisualVM 所说的 Tomcat RAM 消耗不匹配。

从 htop 来看,Tomcat JVM 有 993 MB 的常驻内存

来自 jVisualVM,Tomcat JVM 正在使用

  • 堆最大值:1,070,399,488 B
  • 堆大小:298.438.656 B
  • 使用的堆:变量,介于 170MB 和 270MB 之间
  • 永久代最大值:268,435,456 B
  • 永久代大小:248,872,960 B
  • 使用的 PermGen:略有变化,大约 150MB

据我了解,操作系统内存消耗应该是堆大小 + PermGen 大小 ~= 522 MB。但这比我观察到的要少 471 MB。

有人知道我在这里缺少什么吗?

PS:我知道我的最大堆比使用的要高得多,但我假设如果 JVM 不使用它(即堆大小更小)应该没有效果。

谢谢! 马克

【问题讨论】:

  • 你的堆数是否包括老代和年轻代?
  • 在您的帖子中,您提到了两次“PermGem 大小”:“PermGen 大小:248,872,960 B,PermGen 大小:略有变化,大约 150MB”。可以吗?
  • +1 我也想了解这一点。我确实遇到了 pergem 的一些内存不足错误,因此让我对发生的事情有所了解会有所帮助。
  • @Kevin 我假设它既是老一代又是年轻一代

标签: java tomcat memory jvm


【解决方案1】:

在您的应用程序启动期间,JVM 将保留 内存,大致等于您的 Heap Max 值 (-Xmx) 的大小,再加上更多用于其他内容。这可以防止 JVM 在以后不得不返回操作系统来保留更多内存。

即使您的应用程序仅使用 298mb 的堆空间,操作系统仍会保留 993mb。您将需要阅读更多关于保留内存和已提交内存的内容。

在谈论垃圾收集时,您将阅读的大多数文章都是从堆的角度而不是操作系统级别来指代分配。通过在启动时为您的应用程序保留内存,垃圾收集可以在自己的空间中工作。

如果您需要更多详细信息,请阅读文章Tuning Garbage Collection 以下是文档中的一些重要内容

在初始化时,虚拟保留了最大地址空间,但 除非需要,否则不会分配给物理内存。

另请参阅文档中的第 3.2 (iv) 节

在虚拟机初始化时,整个空间用于 堆被保留。保留空间的大小可以用 -Xmx 选项。如果 -Xms 参数的值小于 -Xmx 参数的值,而不是保留的所有空间 立即提交给虚拟机。

【讨论】:

    【解决方案2】:

    据我了解,操作系统内存消耗应该是堆大小 + PermGen 大小 ~= 522 MB。但这比我观察到的要少 471 MB。有人知道我在这里缺少什么吗?

    如果我理解您所看到的问题是memory fragmentation 和其他领域的 JVM 内存开销的组合。我们经常看到生产程序的内存使用量是我们预期的内存设置的 2 倍。

    Memory fragmentation 可能意味着尽管 JVM 认为操作系统已经给了它一些字节数,但由于内存子系统优化,必须给出一定的额外字节数。

    就 JVM 开销而言,标准内存配置中未包含许多其他存储区域。这是good discussion about this。引用:

    以下 是不属于垃圾收集堆的示例 并且是进程所需内存的一部分:

    • 实现 JVM 的代码
    • 用于实现 JVM 的数据结构的 C 手动堆
    • 系统中所有线程的堆栈(应用程序 + JVM)
    • 缓存的 Java 字节码(用于库和应用程序)
    • JIT 机器代码(用于库和应用程序)
    • 所有加载类的静态变量

    【讨论】:

    • 所有答案都给出了很好的提示,但事实证明,这个答案占了大部分额外内存。一点是 XMX 将在 OS 级别保留虚拟内存,但这不算作“已提交”或“常驻”内存(由 XMS 设置)。另一点是,使用 jconsole 我可以占用更多的“非堆”内存(大约 20MB)。最后,其余的内存似乎由 JVM 本身使用,如上所述。
    【解决方案3】:

    操作系统会报告 JVM 使用的内存 + 你的程序使用的内存。所以它总是高于 JVM 报告的内存使用量。 JVM 本身需要一定数量的内存才能执行您的程序,而操作系统无法区分。

    不幸的是,使用系统内存工具并不是跟踪程序内存消耗的非常精确的方法。 JVM 通常会分配大块内存,因此对象创建速度很快,但这并不意味着您的程序正在消耗这些内存。

    了解您的程序实际在做什么的更好方法是运行 jconsole 并查看那里的内存使用情况。这是一个查看内存的非常简单的工具,易于设置。

    【讨论】:

      【解决方案4】:

      我们首先要记住的是:JVM process heap (OS process) = Java object heap + [Permanent space + Code generation + Socket buffers + Thread stacks + Direct memory space + JNI code + JNI allocated memory + Garbage collection],在这个“集合”中,permSpace 通常是最大的块。

      鉴于此,我想这里的关键是JVM选项-XX:MinFreeHeapRatio=n,其中n是从0到100,它指定如果小于n%的堆是空闲的,则应该扩展堆。默认情况下通常为 40(Sun),因此当 JVM 分配内存时,它会获得足够的 40% 空闲(如果你有 -Xms == -Xmx)。它的“双选项”-XX:MaxHeapFreeRatio 通常默认为 70(星期日)。

      因此,在 Sun JVM 中,每次垃圾回收时活动对象的比率保持在 40-70% 之间。如果在 GC 之后只有不到 40% 的堆空闲,则堆被扩展。因此,假设您正在运行 Sun JVM,我猜“java 对象堆”的大小已达到约 445Mb 的峰值,从而产生约 740 Mb 的扩展“对象堆”(以保证 40% 的空闲) .那么,(对象堆)+(永久空间)= 740 + 250 = 990 Mb。

      也许你可以尝试输出 GC 细节或使用 jconsole 来验证堆大小的演变。

      P.S.:在处理此类问题时,最好发布 OS 和 JVM 详细信息。

      【讨论】:

      • 我今天刚刚调整了 JVM,发现当 Xms==Xmx=3000m 时,从操作系统的角度来看,完整的 ram 分配达到了大约 8-10GB...我什至在使用时陷入了 OOM 杀手的境地Xmx=7000 因为进程占用了几乎所有可用的 16GB 内存并且内核杀死了该进程 :-) 我通常看到如果 Xmx=1g 然后在操作系统级别上最大值为 1,5,但这是不同的故事,不明白此时此刻。从操作系统的角度来看,它强制我将 Xmx 降低最大 RAM 的 2.5 倍。
      • 实际上,根据Oracle docs 之一,他们指出:例如,在配置了 1GB JVM 的 Linux 上运行的 HotSpot JVM 消耗大约 1.2GB 的 RAM
      猜你喜欢
      • 1970-01-01
      • 2011-10-03
      • 2012-10-15
      • 2019-09-14
      • 2013-08-18
      • 1970-01-01
      • 1970-01-01
      • 2015-11-03
      • 2015-02-27
      相关资源
      最近更新 更多