背景
我有一个 Spring Batch 程序,它读取一个文件(我正在处理的示例文件大小约为 4 GB),对文件进行少量处理,然后将其写入 Oracle 数据库。
我的程序使用 1 个线程来读取文件,并使用 12 个工作线程来进行处理和数据库推送。
我正在大量使用 young gen 内存,这导致我的程序比我认为的应该慢。
设置
JDK 1.6.18
Spring Batch 2.1.x
4 核机器,16 GB RAM
-Xmx12G
-Xms12G
-NewRatio=1
-XX:+UseParallelGC
-XX:+UseParallelOldGC
问题
使用这些JVM参数,我的老年代大约可以获得5.x GB的内存,年轻代大约也可以获得5.x GB左右的内存。
在处理这个文件时,我的老年代很好。它最多增长到3 GB,我不需要进行一次完整的GC。
然而,年轻代会达到它的最大值很多次。它会增长到5 GB左右,然后发生并行的小型GC,并将年轻代清除至500MB已用状态。小型GC比完整GC更好,但仍会使我的程序变慢(我相信当年轻代垃圾回收发生时,应用程序仍会冻结,因为我看到数据库活动停止)。我花费了超过5%的时间在小型GC上被冻结,这似乎有点过度。我会说在处理这个4GB文件的过程中,我耗费了50-60GB的年轻代内存。
我没有看到程序中明显的缺陷。我试图遵守通用的OO原则并编写干净的Java代码。我尽量避免无意义地创建对象。我正在使用线程池,并在可能的情况下传递对象而不是创建新对象。我将开始对应用程序进行性能分析,但我想知道是否有一些好的通用规则或反模式可以避免导致过度的内存使用?处理4GB文件需要50-60GB的内存使用是最好的结果吗?我必须回到像对象池这样的JDK 1.2技巧吗?(尽管Brian Goetz展示了为什么对象池是愚蠢的,并且我们不再需要它。我相信他比我自己更可靠.. :))