当前位置: 首页 > 面试题库 >

即使堆等大小稳定,为什么Sun JVM仍继续消耗更多的RSS内存?

孔运珧
2023-03-14
问题内容

在过去的一年中,我在应用程序的Java堆使用方面取得了巨大的进步-
减少了66%。为此,我一直在通过SNMP监视各种指标,例如Java堆大小,cpu,Java非堆等。

最近,我一直在监视JVM有多少实际内存(RSS,驻留集),这让我有些惊讶。JVM消耗的实际内存似乎完全独立于我的应用程序堆大小,非堆,eden空间,线程数等。

通过Java SNMP Java堆使用的图形 衡量的堆大小 http://lanai.dietpizza.ch/images/jvm-heap-
used.png

实内存(KB)。 (例如:1 MB的KB = 1 GB)
Java堆使用的图表http://lanai.dietpizza.ch/images/jvm-
rss.png

(堆图中的三个下降对应于应用程序更新/重新启动。)

这对我来说是个问题,因为JVM消耗的所有额外内存都是“窃取”内存,操作系统可以将其用于文件缓存。实际上,一旦RSS值达到〜2.5-3GB,我开始发现应用程序的响应时间变慢并且CPU利用率更高,这主要是为了等待IO。随着对交换分区的分页开始生效,这是非常不可取的。

所以,我的问题是:

  • 为什么会这样呢? 什么是去 “引擎盖下”
  • 如何控制JVM的实际内存消耗?

血腥细节:

  • RHEL4 64位(Linux-2.6.9-78.0.5.ELsmp#1 SMP Wed Sep 24 … 2008 x86_64 … GNU / Linux)
  • Java 6(内部版本1.6.0_07-b06)
  • 雄猫6
  • 应用程序(点播HTTP视频流)
    • 通过java.nio FileChannels实现高I / O
    • 几百到几千个线程
    • 数据库使用率低
    • 春天,冬眠

相关的JVM参数:

-Xms128m  
-Xmx640m  
-XX:+UseConcMarkSweepGC  
-XX:+AlwaysActAsServerClassMachine  
-XX:+CMSIncrementalMode

-XX:+PrintGCDetails 
-XX:+PrintGCTimeStamps  
-XX:+PrintGCApplicationStoppedTime  
-XX:+CMSLoopWarn  
-XX:+HeapDumpOnOutOfMemoryError

我如何衡量RSS:

ps x -o command,rss | grep java | grep latest | cut -b 17-

这将进入一个文本文件,并定期将其读入监视系统的RRD数据库中。注意ps输出千字节。

虽然它到底是 ATorras
的答案被证明最终是正确的,它
kdgregory
谁引导我与使用的正确诊断路径pmap。(去投票给他们两个答案!)这是正在发生的事情:

我肯定知道的事情:

  1. 我的应用程序使用JRobin 1.4记录和显示数据,这是三年前我编写的代码。
  2. 该应用程序的最繁忙实例当前正在创建
    1. 在启动后的一个小时内,有1000多个新的JRobin数据库文件(每个文件约1.3MB)
    2. 每天约100+
  3. 如果有东西要写,该应用程序每15秒钟更新一次这些JRobin数据库对象。
  4. 在默认配置JRobin中:
    1. 使用java.nio基于文件的后端访问。该后端映射MappedByteBuffers到文件本身。
    2. 每五分钟一次,JRobin守护程序线程MappedByteBuffer.force()在每个JRobin基础数据库MBB上调用
  5. pmap 列出:
    1. 6500映射
    2. 其中5500个是1.3MB的JRobin数据库文件,大约可达到7.1GB

最后一点是我的 “尤里卡!” 时刻。

我的纠正措施:

  1. 考虑更新到最新的JRobinLite 1.5.2,显然更好
  2. 在JRobin数据库上实施适当的资源处理。此刻,一旦我的应用程序创建了一个数据库,然后在不再主动使用该数据库之后再也不会将其转储。
  3. 请尝试将迁移MappedByteBuffer.force()到数据库更新事件,而不要使用定期计时器。问题会神奇地解决吗?
  4. 立即 ,将JRobin后端更改为java.io实现-一行行更改。这会比较慢,但是可能不是问题。以下图表显示了此更改的直接影响。

Java RSS内存使用图http://lanai.dietpizza.ch/images/stackoverflow-rss-problem-
fixed.png

我可能没有时间想出的问题:

  • JVM内部发生了MappedByteBuffer.force()什么?如果没有任何变化,它是否仍会写入整个文件?文件的一部分?它首先加载吗?
  • RSS上是否始终都有一定数量的MBB?(RSS大约是分配的MBB大小总数的一半。巧合吗?我怀疑不是。)
  • 如果我将转移MappedByteBuffer.force()到数据库更新事件,而不是使用定期计时器,问题会神奇地消失吗?
  • 为什么RSS斜率如此规则?它与任何应用程序负载指标都不相关。

问题答案:

只是一个想法:NIO缓冲区位于JVM外部。

编辑: 根据2016年,值得考虑@Lari Hotari评论即使堆等大小稳定时,为什么Sun
JVM仍继续消耗更多的RSS内存?,因为回到2009年,RHEL4的glibc <2.10(〜2.3)

为什么会这样呢?“幕后”发生了什么?

JVM使用的内存不仅仅是堆。例如,Java方法,线程堆栈和本机句柄与堆以及JVM内部数据结构分开分配在内存中。

在您的情况下,可能的麻烦原因可能是:NIO(已经提到),JNI(已经提到),过多的线程创建。

关于JNI,您曾写道该应用程序未使用JNI,但是…您正在使用哪种类型的JDBC驱动程序?可能是2型泄漏吗?但是,正如您所说的那样,数据库使用率很低是非常不可能的。

关于过多的线程创建,每个线程都有自己的堆栈,该堆栈可能很大。堆栈大小实际上取决于VM,OS和体系结构,例如对于JRockit,Linux x64上的堆栈大小为256K,我在Sun文档中没有找到有关Sun VM的参考。这直接影响线程内存(线程内存=线程堆栈大小*线程数)。而且,如果您创建和销毁大量线程,则内存可能不会被重用。

如何控制JVM的实际内存消耗?

老实说,对我而言,成百上千的线程似乎是巨大的。也就是说,如果您确实需要那么多线程,则可以通过该-Xss选项配置线程堆栈大小。这样可以减少内存消耗。但是我认为这不会解决整个问题。我倾向于认为当我查看实际内存图时,某处会有泄漏。

问候。



 类似资料:
  • 我为我的Java web应用程序分配了一个最大值。由于一些内存泄漏,应用程序已经消耗了将近2 GB的分配内存。此时,我已经使用进行了内存转储。在一个实例中,堆转储大小接近>1.5GB,而在另一个实例中,堆转储大小<100 MB。这背后的原因是什么?

  • 我们将和设置为1536m。现在,如果我理解正确的话,j-xmx表示堆的最大大小。 本系统采用4核15GB ram进程。 但是,当我检查正在运行的Java进程的RSS(使用top)时,我看到它使用的值比大,大约。 对于来说,有4个内核和15GB RAM的理想设置是什么(假设除了Java应用程序之外,系统中没有其他进程在运行)

  • 问题内容: 我们正试图在我们的Web应用程序中找到导致大量内存泄漏的元凶。我们在发现内存泄漏方面经验有限,但是我们发现了如何使用Eclipse MAT进行Java堆转储并对其进行分析。 但是,对于我们使用56 / 60GB内存的应用程序,堆转储的大小仅为16GB,而在Eclipse MAT中则更少。 语境 我们的服务器将Ubuntu 14.04上的Wildfly 8.2.0用于我们的Java应用程

  • 我们正试图找到web应用程序中大内存泄漏的罪魁祸首。我们在查找内存泄漏方面的经验相当有限,但我们了解了如何使用创建java堆转储,并在Eclipse mat中对其进行分析。 但是,由于我们的应用程序使用56/60GB内存,堆转储的大小只有16GB,在Eclipse mat中就更少了。 对于我们的java应用程序,我们的服务器使用Ubuntu14.04上的Wildfly8.2.0,其进程使用了95%

  • 问题内容: 该表包含大约一千万行。 这会导致内存使用量稳定增加到4 GB左右,这时行将快速打印。第一行打印之前漫长的延迟使我感到惊讶–我希望它几乎可以立即打印。 我也尝试过以相同的方式表现。 我不知道Django将什么加载到内存中或为什么这样做。我希望Django在数据库级别遍历结果,这意味着结果将以大致恒定的速率打印(而不是经过漫长的等待一次全部打印)。 我误会了什么? (我不知道它是否相关,但

  • 问题内容: 这是 不是 增加Java的堆的最大尺寸的虚拟机启动后。技术原因是什么?垃圾回收算法是否取决于要使用固定数量的内存?还是出于安全原因,通过消耗所有可用内存来防止Java应用程序从DOS的系统中移至其他应用程序? 问题答案: 最后我知道在Sun的JVM中,必须在连续的地址空间中分配整个堆。我想对于大堆值,很难在启动后将其添加到您的地址空间中,同时又要确保它保持连续。您可能需要在启动时获取它