我有一个Java程序,它是一个典型的机器学习算法,通过一些方程式更新一些参数的值:
更新参数的计算相当复杂,我必须创建许多临时对象,但它们不会在循环外被引用。循环内的代码对CPU要求较高,不涉及磁盘访问。该程序加载了一个相对较大的训练数据集,因此我将JVM分配了10G内存(-Xmx10G),这比它实际需要的要大得多(通过“top”命令或窗口任务管理器峰值为~6G)。
我在几台linux机器(centos 6,24G内存)和一台Windows机器(win7,12G)上进行了测试,都安装了SUN Hotspot JDK/JRE 1.8。我没有指定除-Xmx之外的其他JVM参数。这两台机器都专门运行我的程序。
在Windows上,我的程序运行良好:每次迭代使用的运行时间非常相似。然而,在所有centos机器上的运行时间都很奇怪。它最初正常运行,但在第7/8次迭代时显著减慢(变慢约10倍),然后在以后的每次迭代中保持减慢约10%。
我怀疑这可能是由Java的垃圾回收器引起的。因此,我使用jconsole来监视我的程序。在两台机器上,Minor GC非常频繁,这是因为程序在循环中创建了许多临时变量。此外,我使用了“jstat -gcutil $pid$ 1s”命令并捕获了统计信息:
Centos:https://www.dropbox.com/s/ioz7ai6i1h57eoo/jstat.png?dl=0 Window:https://www.dropbox.com/s/3uxb7ltbx9kpm9l/jstat-winpng.png?dl=0 [编辑] 然而,两种机器上的统计数据差异很大:
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内存)和一台Windows机器(win7,12G)上进行了测试,都安装了SUN Hotspot JDK/JRE 1.8。我没有指定除-Xmx之外的其他JVM参数。这两台机器都专门运行我的程序。
在Windows上,我的程序运行良好:每次迭代使用的运行时间非常相似。然而,在所有centos机器上的运行时间都很奇怪。它最初正常运行,但在第7/8次迭代时显著减慢(变慢约10倍),然后在以后的每次迭代中保持减慢约10%。
我怀疑这可能是由Java的垃圾回收器引起的。因此,我使用jconsole来监视我的程序。在两台机器上,Minor GC非常频繁,这是因为程序在循环中创建了许多临时变量。此外,我使用了“jstat -gcutil $pid$ 1s”命令并捕获了统计信息:
Centos:https://www.dropbox.com/s/ioz7ai6i1h57eoo/jstat.png?dl=0 Window:https://www.dropbox.com/s/3uxb7ltbx9kpm9l/jstat-winpng.png?dl=0 [编辑] 然而,两种机器上的统计数据差异很大:
- 在Windows上,“S1”快速跳动在0到50之间,而在CentOS上停留在“0.00”。
- 在Windows上,“E”从0到100变化非常迅速。由于我每秒打印一次状态,屏幕截图没有显示其增加到100。然而,在CentOS上,“E”向100缓慢增加,然后减少到0,再次增加。
看来我的程序的奇怪行为是由Java GC引起的?我对Java性能监视器还不熟悉,也不知道如何优化GC参数设置。你有什么建议吗?非常感谢!