Ahuaxuan在公司属于大牛级别的人物,技术理解深入,口才更好,人品也好。
转载这篇文章,想做个JVM方面的专题:
Ahuaxuan的这篇文章写得很好,做个转载,过段时间准备制成PDF。
/*
- @author: ahuaxuan
- @date: 2010-4-30
- /
在内存充裕的情况下的OOM
理解本文的前提是理解JVM的内存模型:包括
perm, old, young(eden, from(s0), to(s1)),
然后理解young中的垃圾搜集算法(拷贝算法,尤其是eden, from(s0),
to(s1)它们分别扮演什么样的角色,为什么任意时间中from和to中必须要有一个是空的), old里的垃圾搜索算法(常见的有:标记压缩算法).
正文:
最近一台测试服务器抛出了OOM的错误,本着不放过一切问题的原则,ahuaxuan把这个OOM研究了一番,最后得出了令人意想不到的结论.
charpter 1, 现象
看到这个OOM的信息之后,ahuaxuan的第一反应是,内存是否真的不够用了,于是使用jmap查看了一下内存的情况,如下:
我们的测试服务器给JVM进程分配的空间是4G,但是从上图中,我们可以看出内存并没有完全用完,尤其是From和TO这两个suvivor空间居然还有900M的空间剩余. Eden 也有112M的空间剩余,Old有288M的空间剩余.
这个不像是空间不足的情况.除非我们的对象真的有这么大,大到超过288M,不管eden还是old都放不下.一个对象要超过300M,那这是一个什么样的对象呀,初步分析,这个对象应该是一个byte数组或者char数组.
charpter2 大对象来源
为了追到这个问题,接下来就是把heap的dump信息拿出来,通过 jmap -dump:format=b,file=heap.bin <pid> 把当时的这个进程的dump拿了下来(2.7G).
然后用MAT打开一看.
内存基本被byte和char霸占了.寻找这些byte和char的root.得到
嘿,好家伙,360M的对象,还不在少数(还有其他的图,包含了其他的大对象,就不列出了,主角就是上面的char[]), 而且这些对象是由Lucene创建的,经查,lucene会将索引中的field装载入内存,看似也是合理的,但是不合理的地方是它出现在了 RAMInputStream这个类中,因为按照jackrabbit的逻辑(ahuaxuan曾经写过17篇文章来阐述jackrabbit中的搜索模 块,详情查看: http://ahuaxuan.iteye.com/category/65829 ).这里ahuaxuan稍微阐述一下jackrabbit的搜索模块中是怎么使用RAMDirectory的:
新 的索引请求到达索引模块,建索引完成之后,jackrabbit不会立即将索引数据刷入磁盘, 而是放在内存中,然后当内存中的数据满足一定量之后(可以在配置文件中设置),这批数据会被一起刷到磁盘中,也就是从RAMDirectory中刷到 FSDirectory中,而刚才讲到满足一定的量,其实默认值是100, 也就是说RAMDirectory中最多保持了100个Document.
问题就在这里,100个Document能有360M的field?
这个可能性微乎其微. 如果不是这360M的量有问题,那么就是这100个Document的数量有问题,也许根本就超过了100个.
回过头来我们再来看看上面的逻辑: