我有一个java程序,它是一种典型的机器学习算法,用某些方程更新某些参数的值:
for (int iter=0; iter<1000; iter++) {
// 1. Create many temporary variables and do some computations
// 2. Update the value for the parameters
}
更新参数的计算相当复杂,我必须创建许多临时对象,但它们不会被循环引用。循环中的代码是CPU密集型的,不访问磁盘。这个程序加载了一个相对较大的训练数据集,因此,我给JVM授予了10G内存(-Xmx10G),这比它要求的要大得多(“top”命令或窗口的任务管理器在~6G处达到峰值)。
我在几台linux机器(centos 6,24G内存)和一台窗口机器(win7,12G)上进行了测试,两者都安装了SUN Hotspot JDK / JRE 1.8。除了-Xmx之外,我没有指定其他JVM参数。这两台机器都专用于我的程序。
在Windows上,我的程序运行良好:每次迭代都使用非常相似的运行时间。但是,所有centos机器的运行时间都很奇怪。 它最初运行正常,但在第7次/第8次迭代时显着减慢(慢10倍),然后在每次迭代后保持减速~10%。
我怀疑它可能是由Java的垃圾收集器引起的。因此,我使用jconsole来监控我的程序。次要GC在两台机器上经常发生,这是因为程序在循环中创建了许多临时变量。此外,我使用“jstat -gcutil $ pid $ 1s”命令并捕获统计信息:
Centos:https://www.dropbox.com/s/ioz7ai6i1h57eoo/jstat.png?dl=0
窗口:https://www.dropbox.com/s/3uxb7ltbx9kpm9l/jstat-winpng.png?dl=0
[编辑]但是,两种机器的统计数据差异很大:
我的程序的奇怪行为似乎是由于Java GC?我是Java性能监视器的新手,并且没有一个好主意来优化GC参数设置。你有什么建议吗?非常感谢你!
答案 0 :(得分:1)
很抱歉发布此答案,但我没有足够的分数来评论。
如果您认为它与GC有关,我会更改垃圾1收集器-XX:+ UseG1GC
我发现了这个简短的解释: http://blog.takipi.com/garbage-collectors-serial-vs-parallel-vs-cms-vs-the-g1-and-whats-new-in-java-8/
您可以在分析下运行您的软件吗?尝试使用jprofiler,VisualVM甚至netbeans探查器。它可能会对你有所帮助。
我注意到你有自己的矢量和矩阵封装。 也许你的内存也比必要多得多。但我认为这不是问题所在。
再次抱歉没有作为评论做出贡献。 (这会更合适)
答案 1 :(得分:1)
给Java(或任何垃圾收集语言)过多的内存会对性能产生负面影响。实时(引用)对象在内存中变得越来越稀疏,导致从主内存中更频繁地获取。请注意,在您向我们展示的示例中,更快的窗口比Linux更快和完整GC - 但GC周期(尤其是完整的gcs)通常对性能不利。
如果运行训练集不会花费特别长的时间,那么尝试在不同的内存分配基准测试。
一个更激进的解决方案,但应该产生重大影响的解决方案是通过回收池中的对象来消除(或尽可能减少)循环内的对象创建。
答案 2 :(得分:0)
我会考虑在循环外声明变量,因此mem分配完成一次并完全消除GC。
答案 3 :(得分:0)
首先,在循环之外声明变量以避免使用garbace是一种常见的最佳做法。 作为&#39; Wagner Tsuchiya&#39;说,如果你对GC有疑问,试试运行一个分析器。 如果你想要一些关于GC调整的技巧,我发现很好blogpost。
答案 4 :(得分:0)
您可以尝试每次迭代调用System.gc()以查看性能是上升还是下降。这可以帮助您将其缩小到以前的一些答案诊断。
答案 5 :(得分:0)
如果你的截图中显示的GC时间是几百毫秒,那么GC可能不是问题。我建议您使用分析器来调查锁争用和IO(Netbeans很棒)。我知道你说你的程序做了很少的IO但是有了分析(就像调试一样)你必须删除所有的假设并一步一步地去。
答案 6 :(得分:0)
根据我的经验,JAVA需要足够的内存和2+ CPU。否则,GC开始运行时CPU使用率将非常大。