背景
我有一个 Spring 批处理程序,它读取一个文件(我正在使用的示例文件大小约为 4 GB),对文件进行少量处理,然后将其写入 Oracle 数据库。
我的程序使用 1 个线程读取文件,使用 12 个工作线程进行处理和数据库推送。
我正在搅动很多很多很多年轻一代的记忆,这导致我的程序运行得比我想象的要慢。
设置
JDK 1.6.18
Spring batch 2.1.x
4 Core Machine w 16 GB ram
-Xmx12G
-Xms12G
-NewRatio=1
-XX:+UseParallelGC
-XX:+UseParallelOldGC
问题
使用这些 JVM 参数,我可以为 Tenured Generation 获得大约 5.x GB 的内存,为 Young Generation 获得大约 5.X GB 的内存。
在处理这一个文件的过程中,我的 Tenured Generation 很好。它最大可能增长到 3 GB,而且我永远不需要进行一次完整的 GC。
然而,年轻一代多次达到最大值。它上升到 5 GB 范围,然后发生并行次要 GC 并将 Young Gen 清除到使用的 500MB。次要 GC 比完整 GC 好且更好,但它仍然会大大降低我的程序速度(我很确定当发生年轻一代收集时应用程序仍然冻结,因为我看到数据库活动消失了)。我为次要 GC 冻结了超过 5% 的程序时间,这似乎过多。我想说,在处理这个 4 GB 文件的过程中,我搅动了 50-60GB 的年轻一代内存。
我在我的程序中没有看到任何明显的缺陷。我试图遵守一般的 OO 原则并编写干净的 Java 代码。我试图不要无缘无故地创建对象。我正在使用线程池,并尽可能传递对象而不是创建新对象。我将开始分析应用程序,但我想知道是否有人有一些好的一般经验法则或反模式以避免导致过多的内存流失?50-60GB 的内存搅动来处理一个 4GB 的文件是我能做的最好的吗?我是否必须恢复到对象池之类的 JDK 1.2 技巧?(尽管 Brian Goetz 做了一个演讲,其中包括为什么对象池是愚蠢的,我们不需要再这样做了。我相信他比我相信自己多得多.. :))