非堆内存泄漏JVM

我有一个运行在oracle java虚拟机上的Ubuntu服务器上设置的glassfish v4.0,jvm进程常驻内存大小(通过“top”命令)成长,直到jvm没有内存来创build新线程。

我拥有的:

  • 1Gb ram和1.4GHz处理器的VPS服务器(1核)
  • Ubuntu服务器12.04
  • Java(TM)SE运行时环境(build 1.7.0_51-b13)
  • Java HotSpot(TM)64位服务器虚拟机(构build24.51-b03,混合模式)
  • Glassfish v4.0运行我的Java EE web应用程序
  • VM以下列参数运行-XX:MaxPermSize = 200m -XX:PermSize = 100m -XX:Xmx = 512m(我可以添加所有相关参数)

有什么问题:

RAM使用率(res memory)一直在增长,具体取决于每小时10-100m的使用率,直到jvm无法分配本地内存。

我试过了什么:

  • 我已经降低了最大堆空间,这只能节省时间,直到jvm崩溃
  • 我附上了plumbr( https://portal.plumbr.eu/ ),它没有检测到堆中有任何内存泄漏
  • 我也设置最大烫发大小为较低的价值。

我想让我的JVM稳定,因为我测量堆空间+ perm gen只需要400-600 mb,而“top”命令显示java进程内存增长到850mb,然后杀死它自己。 我知道JVM需要更多的内存空间和堆,但是你认为我还是给堆空间和perm gen留下了太多的内存吗? 任何帮助或指导将不胜感激。

日志输出: http ://pakers.lv/logs/hs_err_pid970.log所有JVM flgas: http ://pakers.lv/logs/jvm_flags.txt

更新

我还尝试了什么(根据build议和我自己的发现):

  • 我已经将堆空间减less到了256m,然后在系统稳定的时候增加了空间,我注意到我能承受的最大堆是512m和128m perm的gen space。 (-Xmx512m,-Xms512m,-XX:PermSize = 128m,-XX:MaxPermSize = 128m)
  • 减lessjava线程大小-Xss256k,我无法减less它小于218k(jvm won`t start)
  • 增加了-D64,以便jvm以64位模式运行
  • 已添加-XX:+ AggressiveOpts(用于启用性能优化),-XX:+ UseCompressedOops(用于减less堆空间内存使用率),-server标志用于在服务器模式下启动jvm
  • 由于堆空间大小非常有限,因此我修改了NewRatio,使其拥有一个较大的年龄(堆空间的1/3)-XX:NewRatio = 3
  • 添加GC诊断选项,以便检查OOM错误-XX:+ PrintTenuringDistribution -XX:+ PrintGCDetails -XX:+ PrintGCTimeStamps -XX:+ HeapDumpOnOutOfMemoryError -Xloggc:/home/myuser/garbage.log

当前状态通过这些更改,我最终限制了作为我的目标的java进程的驻留内存(内存使用量)。 在我的情况下,512m的堆空间+128m的perm gen space导致了java进程中常驻内存750m左右的稳定。 即使我仍然有内存问题 – 堆内存不时得到充分,并导致Web应用程序冻结,由于继续垃圾收集,但操作系统不杀死进程。 所以我现在需要增加系统的可用内存(RAM),或者检查堆的使用情况,降低应用程序的占用空间。 由于我的Web应用程序是基于Java EE的(使用EJB),因此我可能无法大幅减less它。 无论如何感谢您的build议,并随时分享任何其他build议,如果有的话。

给出你分享的内容有几种可能性,例如:

  • 一个泄露的JNI库,或者,
  • 线程创build泄漏,或
  • 泄漏的dynamic代理代理(perm-gen泄露),

但我只能猜测,因为您没有提供任何日志输出,或者指出JVM是否抛出了OutOfMemoryException (OOM),或者是否遇到其他错误。 你也没有提到垃圾收集器在使用什么,但是如果上面显示的标记是唯一使用的JVM选项,那么它就是CMS收集器。

第一步是通过添加这些标志来使垃圾收集器的操作可观察:

 -XX:+PrintTenuringDistribution -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+HeapDumpOnOutOfMemoryError -Xloggc:/path/to/garbage.log 

如果它确实是一个OOM,你可以使用VisualVM或类似工具分析堆转储。 我也使用VisualVM通过JMX原位监视GC操作。 通过这些JVM标志可以启用对JVM内部的可见性:

 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=4231 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false 

其他资源:

更新

日志确实有帮助。 谢谢。 那个特定的日志显示它在物理内存耗尽之前可能会增长到最大configuration。 它试图malloc〜77M,只有〜63M物理左:

本地内存分配(malloc)未能分配77873152字节提交保留内存。

..

/ proc / meminfo:MemTotal:1018724 kB MemFree:63048 kB

这是我会做的:

  1. 减less堆,使其“适合”在机器上。 将最小和最大堆设置为相同的值,以便可以判断它是否会立即适合 – 如果不适合,它将不会启动。

  2. 你可以减lessJava堆栈的大小( -Xss ),但是这个东西似乎并没有-Xss很多的线程,所以节省的空间不会超过1Mb。 我认为64位Linux的默认值是256k。 减less太多,它会启动堆栈分配的OOM。

  3. 重复testing。

  4. 当它在负载下运行一会儿时,使用jmap -dump:file=path_to_file <pid>产生按需堆转储以进行差异诊断。

  5. 其中一件事应该发生:(a)如果发生泄漏,最终会再次失败,但是OOM的types应该是不同的,或者(b)没有泄漏使得GC更加努力地工作,你完成了。 鉴于你之前曾经尝试过,前一种情况很可能,除非你减小的最大尺寸也不适合。

  6. 如果它没有OOM,比较两个转储,看看使用jhat或其他堆分析器增长。

祝你好运!

尝试通过将-D64添加到JVM启动标志来以64位模式运行进程。

您可以运行pmap $JVMPID来查看虚拟内存是如何分配的。 在崩溃之前运行它。