论坛首页 Java版 企业应用

一次奇怪的Out of Memory 分析(附图)

浏览 3266 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
最后更新时间:2007-10-31 关键字: Spring iBatis Hessian
以前没有遇到过这种OOM,说奇怪是因为在Windows资源管理器中查看java进程的内存占用是正常情况,即增加和减少相持平。查看gc.log,Jprofiler进行跟踪调试,均正常,但是还是提示出现如下错误:
[16:52:20] java.lang.OutOfMemoryError: Java heap space
[16:52:20] 	at com.rec.cell.common.ParseInputCommand.parseIdentPicCommand(ParseInputCommand.java:382)
[16:52:20] 	at com.rec.cell.login.service.impl.IdentPicServiceImpl.identPicFvalue(IdentPicServiceImpl.java:97)
[16:52:20] 	at sun.reflect.GeneratedMethodAccessor31.invoke(Unknown Source)
[16:52:20] 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
[16:52:20] 	at java.lang.reflect.Method.invoke(Method.java:585)
[16:52:20] 	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:304)
[16:52:20] 	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:172)


我的系统平台是
Win2003
Resin 3.12 Pro
JDK 1.5 update 12

JVM配置参数为:
      <jvm-arg>-server</jvm-arg>
      <jvm-arg>-verbose:gc</jvm-arg>
      <jvm-arg>-Xloggc:gc.log</jvm-arg>
      <jvm-arg>-XX:+PrintGCDetails</jvm-arg>
      <jvm-arg>-XX:+PrintGCTimeStamps</jvm-arg>
      <jvm-arg>-noclassgc</jvm-arg>
      <jvm-arg>-Xms512m</jvm-arg>
      <jvm-arg>-Xmx512m</jvm-arg>
      <jvm-arg>-XX:NewRatio=3</jvm-arg>
      <jvm-arg>-XX:SurvivorRatio=8</jvm-arg>
      <jvm-arg>-XX:PermSize=64m</jvm-arg>
      <jvm-arg>-XX:MaxPermSize=128m</jvm-arg>
      <jvm-arg>-Xss1m</jvm-arg>



Web系统组成为Spring 2.0+iBatis(DBCP)+Hessian,单线程跑此应用。
所以系统中大量使用了reflect功能,在初次运行时,过2分钟即出现如下错误:
Unloading class sun.reflect.GeneratedMethodAccessor*
查找JVM配置,加上-noclassgc,使jvm不卸载加载的class,然后这个情况消失。因为系统中只有有限数量的class,并非网站类系统有n个jsp页面,所以-noclassgc加上是不会产生Permsize太小的错误的。

但是OOM的问题出现了

GC log如下:

4.865: [GC [PSYoungGen: 76702K->12689K(114688K)] 76702K->12689K(507904K), 0.0616025 secs]
4.927: [Full GC [PSYoungGen: 12689K->0K(114688K)] [PSOldGen: 0K->12622K(393216K)] 12689K->12622K(507904K) [PSPermGen: 10187K->10187K(65536K)], 0.0791577 secs]
8.925: [GC [PSYoungGen: 98304K->16371K(114688K)] 110926K->40147K(507904K), 0.1828853 secs]
42.080: [GC [PSYoungGen: 114675K->16377K(114688K)] 138451K->54191K(507904K), 0.0966398 secs]
106.919: [GC [PSYoungGen: 114681K->16381K(114688K)] 152495K->61151K(507904K), 0.0918806 secs]
206.359: [GC [PSYoungGen: 114685K->16371K(114688K)] 159455K->61141K(507904K), 0.0651641 secs]
312.719: [GC [PSYoungGen: 114675K->16374K(80256K)] 159445K->61144K(473472K), 0.0696641 secs]
380.916: [GC [PSYoungGen: 80246K->16409K(79936K)] 125016K->61179K(473152K), 0.0738506 secs]
446.445: [GC [PSYoungGen: 79866K->1819K(93824K)] 124636K->61326K(487040K), 0.0668315 secs]
512.122: [GC [PSYoungGen: 62683K->160K(60736K)] 122190K->61304K(453952K), 0.0301872 secs]
518.359: [GC [PSYoungGen: 6089K->139K(93120K)] 67234K->61288K(486336K), 0.0238365 secs]
518.383: [GC [PSYoungGen: 139K->123K(56704K)] 61288K->61308K(449920K), 0.0228543 secs]
518.406: [Full GC [PSYoungGen: 123K->0K(56704K)] [PSOldGen: 61184K->35359K(393216K)] 61308K->35359K(449920K) [PSPermGen: 20703K->20703K(65536K)], 0.1965706 secs]
518.602: [GC [PSYoungGen: 0K->0K(93760K)] 35359K->35359K(486976K), 0.0229942 secs]
518.625: [Full GC [PSYoungGen: 0K->0K(93760K)] [PSOldGen: 35359K->12463K(393216K)] 35359K->12463K(486976K) [PSPermGen: 20703K->20306K(65536K)], 0.1782066 secs]


用Jprofiler跟踪系统运行情况,入下图:

Heap整体运行情况:


其中Eden区运行情况:


其中Old区运行情况:


其中Survivor区运行情况:


其中GC运行情况:


出现OOM的时间:就在最后一次Full Gc的时候
出现OOM的地点:语句为 new了一个76800大小的byte[],这条语句是在一个static函数里边的,在函数退出时理论上会被回收。

现象和疑问如下:
1.观察windows资源管理器。GC的log以及Jprofiler的图形显示,java内存以及本地内存并没有突然增加的迹象,一直保持在200M左右浮动。Resin的status页面显示的内存占用也是正常情况,为何在代码执行到此处时会出现OOm
2.新生代空间(PSYoungGen)一直在减小,从开始的100M左右减小到最后的60M左右。但是新生代的垃圾回收是正常的,从图中可以看出垃圾回收的过程和区总容量减小的过程。
3.Old区缓慢增加中,但是在最后一次gc的时候减小,old区容量并不大
4.Survivor区从一开始就一直处于Full的状态(全部被占用),但是当新生代空间减小的时候,Survivor区容量突然增加并且翻番,占用状况变的很小。在OOM的适合,此区容量变的接近于0

基于以上分析,尝试调整XX:SurvivorRatio=4,则OOM的出现时间更短。
实在是很疑惑,为什么会出现这种情况的OOM,以前遇见的都是系统内存飚升到web app出现OOM,但是现在外表看起来内存正常,但是发生OOM。请各位有经验的帮忙分析分析。 我查看资料,有说是因为jvm的原因导致内存碎片太多,致使分配大空间的时候不能连续分配从而引起OOM,不知道是不是这种原因。
   
最后更新时间:2007-11-07
也许是pergemspace吧
   
0 请登录后投票
最后更新时间:2007-11-01
static函数+static数组+reflect生成的类。
我不认为能在函数结束后释放。

能不能不用reflection,而用prototype之类的?

另外,楼上的,log 写的 heap space……
   
0 请登录后投票
最后更新时间:2007-11-01
dwangel 写道
static函数+static数组+reflect生成的类。
我不认为能在函数结束后释放。

能不能不用reflection,而用prototype之类的?

另外,楼上的,log 写的 heap space……

是通过spring暴露的hessian接口作为远程调用的入口函数A,然后这个入口函数A里边,调用的static函数进行一些功能性的解析,返回一个HashMap给A函数。其中new的那个byte[76800]就是加到HashMap里边返回给A函数的。也不是全都是static,至少数组不是。真的是不知道为啥会heap OOM了,一切显示都正常。
   
0 请登录后投票
最后更新时间:2007-11-07
无语
引用
Unloading class sun.reflect.GeneratedMethodAccessor*

是正常的现象,说明大量使用反射生成的class被jvm正确回收了。

看来你应该是permgen区outofmermory了,可以尝试在启动配置中加大permgensize,不过不建议太大,不要超过300M,而且这样也只能治标不能治本。

jdk dynamicproxy每次都会生成新的代理类,如果你不卸载这些代理类,permgen区outofmemory是迟早的事情。
如果你真的不想卸载加载到jvm中的代理类,就要考虑复用生成的代理,这样的话,jdk的dynamicproxy是不行的,你要使用cglib来生成可复用的代理类。

因此,你有两条路可以走
1、继续使用jdk的dynamicproxy,但不要禁止class卸载。
2、使用cglibproxy,复用代理类,避免大量生成的代理类。
   
0 请登录后投票
最后更新时间:2007-11-07
看上去不该是permgen的问题,因为permgen的OOM会明确的在异常中提示OutOfMemoryError: PermGen.
这个应该是普通的内存不足,如果不是内存太小(你配置了多少内存,给Resin的JVM设置的最大内存是多少?),那就很可能是你的代码在哪里没有释放引用的对象,比如存在一个全局性的容器类,不断的向里面加入对象,但没有删除的机制。
   
0 请登录后投票
最后更新时间:2007-11-16
我也遇到一个问题, 使用HashMap加载50w的词汇, 老是报
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

难道HashMap连50w都撑不上吗?

我是刚从.net转java的, 不熟悉, 望大家指点
   
0 请登录后投票
最后更新时间:2007-11-16
因为默认heap space大小为64m, 所以当加载到20多w时刚好65m, 直接就报溢出了, 后来加上 -Xms300m -Xmx300m 参数, 顺利通过~
   
0 请登录后投票
最后更新时间:2007-11-16
瞎碰:Xss不要设置,默认好像才2k,它对内存的占用是Xss×线程数量, gclog可以增加选项 -XX:+PrintHeapAtGC  -XX:+PrintTenuringDistribution
   
0 请登录后投票
论坛首页 Java版 企业应用

跳转论坛:
JavaEye推荐