背景
我有一个 Spring 批处理程序,它读取一个文件(我正在使用的示例文件大小约为 4 GB),对文件进行少量处理,然后将其写到 Oracle 数据库中。
我的程序使用 1 个线程来读取文件,并使用 12 个工作线程来进行处理和数据库推送。
我正在搅动大量的年轻一代内存,这导致我的程序比我想象的要慢。
Setup
JDK 1.6.18
春季批次 2.1.x
4 核机器,配备 16 GB 内存
-Xmx12G
-Xms12G
-NewRatio=1
-XX:+UseParallelGC
-XX:+UseParallelOldGC
Problem
通过这些 JVM 参数,我为 Tenured Generation 获得了大约 5.x GB 的内存,为 Young Generation 获得了大约 5.x GB 的内存。
在处理这一文件的过程中,我的终身一代一切都很好。它最多可能增长到 3 GB,而且我永远不需要执行一次完整 GC。
然而,年轻一代多次达到了极限。它达到 5 GB 范围,然后发生并行次要 GC,并将 Young Gen 的使用量降至 500MB。小 GC 很好,而且比完整 GC 更好,但它仍然会大大减慢我的程序速度(我很确定当发生年轻代收集时应用程序仍然会冻结,因为我看到数据库活动消失了)。我花费了超过 5% 的程序时间来冻结小型 GC,这似乎过多了。我会说在处理这个 4 GB 文件的过程中,我使用了 50-60GB 的年轻一代内存.
我没有发现我的程序有任何明显的缺陷。我试图遵守一般的面向对象原则并编写干净的 Java 代码。我试图不无缘无故地创建对象。我正在使用线程池,并尽可能传递对象而不是创建新对象。我将开始分析应用程序,但是我想知道是否有人有一些好的一般经验规则或反模式来避免导致过度的记忆搅动?我能用 50-60GB 内存来处理 4GB 文件吗?我是否必须恢复到 JDK 1.2 技巧,例如对象池? (尽管 Brian Goetz 做了一个演讲,其中包括为什么对象池是愚蠢的,而且我们不需要再这样做了。我对他的信任比我对自己的信任要多..:))
我有一种感觉,你正在花费时间和精力来尝试优化一些你不应该费心的事情。
我花费了超过 5% 的程序时间来冻结小型 GC,这似乎过多了。
把它翻转过来。您花费了不到 95% 的计划时间来做有用的工作。或者换句话说,即使您设法优化 GC 以在零时间内运行,您最多可以获得超过 5% 的改进。
如果您的应用程序有受暂停时间影响的严格计时要求,您可以考虑使用低暂停收集器。 (请注意,减少暂停时间增加总体 GC 开销...)但是对于批处理作业,GC 暂停时间应该不相关。
最重要的可能是整个批处理作业的挂钟时间。并且(大约)95% 的时间花在特定于应用程序的事情上,您可能会为您的分析/有针对性的优化工作获得更多回报。例如,您是否考虑过批量发送到数据库的更新?
所以..我的总内存的 90% 位于“oracle.sql.converter.toOracleStringWithReplacement”中的 char[] 中
这往往表明您的大部分内存使用发生在 Oracle JDBC 驱动程序中,同时准备将内容发送到数据库。你对此的了解很少。我将其视为不可避免的开销。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)