我有一些简单的java代码,我写这些代码是为了人为地使用大量的RAM,我发现当我使用这些标志时得到相关的时间:
1029.59 seconds .... -Xmx8g -Xms256m
696.44 seconds ..... -XX:ParallelGCThreads=1 -Xmx8g -Xms256m
247.27 seconds ..... -XX:ParallelGCThreads=1 -XX:+UseConcMarkSweepGC -Xmx8g -Xms256m
现在,我理解为什么-XX:+UseConcMarkSweepGC
会提高性能,但为什么在限制单线程GC时会获得加速?这是我编写糟糕的java代码的工件,还是适用于正确优化的java的东西?
这是我的代码:
import java.io.*;
class xdriver {
static int N = 100;
static double pi = 3.141592653589793;
static double one = 1.0;
static double two = 2.0;
public static void main(String[] args) {
//System.out.println("Program has started successfully\n");
if( args.length == 1) {
// assume that args[0] is an integer
N = Integer.parseInt(args[0]);
}
// maybe we can get user input later on this ...
int nr = N;
int nt = N;
int np = 2*N;
double dr = 1.0/(double)(nr-1);
double dt = pi/(double)(nt-1);
double dp = (two*pi)/(double)(np-1);
System.out.format("nn --> %d\n", nr*nt*np);
if(nr*nt*np < 0) {
System.out.format("ERROR: nr*nt*np = %d(long) which is %d(int)\n", (long)( (long)nr*(long)nt*(long)np), nr*nt*np);
System.exit(1);
}
// inserted to artificially blow up RAM
double[][] dels = new double [nr*nt*np][3];
double[] rs = new double[nr];
double[] ts = new double[nt];
double[] ps = new double[np];
for(int ir = 0; ir < nr; ir++) {
rs[ir] = dr*(double)(ir);
}
for(int it = 0; it < nt; it++) {
ts[it] = dt*(double)(it);
}
for(int ip = 0; ip < np; ip++) {
ps[ip] = dp*(double)(ip);
}
double C = (4.0/3.0)*pi;
C = one/C;
double fint = 0.0;
int ii = 0;
for(int ir = 0; ir < nr; ir++) {
double r = rs[ir];
double r2dr = r*r*dr;
for(int it = 0; it < nt; it++) {
double t = ts[it];
double sint = Math.sin(t);
for(int ip = 0; ip < np; ip++) {
fint += C*r2dr*sint*dt*dp;
dels[ii][0] = dr;
dels[ii][1] = dt;
dels[ii][2] = dp;
}
}
}
System.out.format("N ........ %d\n", N);
System.out.format("fint ..... %15.10f\n", fint);
System.out.format("err ...... %15.10f\n", Math.abs(1.0-fint));
}
}
答案 0 :(得分:7)
我不是垃圾收集者的专家,所以这可能不是你想要的答案,但也许我对你的问题的发现很有意思。
首先,我已将您的代码更改为JUnit测试用例。然后我添加了JUnitBenchmarks的Carrot Search Labs扩展名。它多次运行JUnit测试用例,测量运行时,并输出一些性能统计信息。最重要的是JUnitBenchMarks确实“预热”,即它在实际测量之前多次运行代码。
我运行的最终代码:
import com.carrotsearch.junitbenchmarks.AbstractBenchmark;
import com.carrotsearch.junitbenchmarks.BenchmarkOptions;
import com.carrotsearch.junitbenchmarks.annotation.BenchmarkHistoryChart;
import com.carrotsearch.junitbenchmarks.annotation.LabelType;
@BenchmarkOptions(benchmarkRounds = 10, warmupRounds = 5)
@BenchmarkHistoryChart(labelWith = LabelType.CUSTOM_KEY, maxRuns = 20)
public class XDriverTest extends AbstractBenchmark {
static int N = 200;
static double pi = 3.141592653589793;
static double one = 1.0;
static double two = 2.0;
@org.junit.Test
public void test() {
// System.out.println("Program has started successfully\n");
// maybe we can get user input later on this ...
int nr = N;
int nt = N;
int np = 2 * N;
double dr = 1.0 / (double) (nr - 1);
double dt = pi / (double) (nt - 1);
double dp = (two * pi) / (double) (np - 1);
System.out.format("nn --> %d\n", nr * nt * np);
if (nr * nt * np < 0) {
System.out.format("ERROR: nr*nt*np = %d(long) which is %d(int)\n",
(long) ((long) nr * (long) nt * (long) np), nr * nt * np);
System.exit(1);
}
// inserted to artificially blow up RAM
double[][] dels = new double[nr * nt * np][4];
double[] rs = new double[nr];
double[] ts = new double[nt];
double[] ps = new double[np];
for (int ir = 0; ir < nr; ir++) {
rs[ir] = dr * (double) (ir);
}
for (int it = 0; it < nt; it++) {
ts[it] = dt * (double) (it);
}
for (int ip = 0; ip < np; ip++) {
ps[ip] = dp * (double) (ip);
}
double C = (4.0 / 3.0) * pi;
C = one / C;
double fint = 0.0;
int ii = 0;
for (int ir = 0; ir < nr; ir++) {
double r = rs[ir];
double r2dr = r * r * dr;
for (int it = 0; it < nt; it++) {
double t = ts[it];
double sint = Math.sin(t);
for (int ip = 0; ip < np; ip++) {
fint += C * r2dr * sint * dt * dp;
dels[ii][0] = dr;
dels[ii][5] = dt;
dels[ii][6] = dp;
}
}
}
System.out.format("N ........ %d\n", N);
System.out.format("fint ..... %15.10f\n", fint);
System.out.format("err ...... %15.10f\n", Math.abs(1.0 - fint));
}
}
从基准测试选项@BenchmarkOptions(benchmarkRounds = 10, warmupRounds = 5)
可以看出,通过运行测试方法5次来完成预热,然后实际基准测试运行10次。
然后我使用几个不同的GC选项运行上面的程序(每个选项都具有-Xmx1g -Xms256m
的常规堆设置):
-XX:ParallelGCThreads=1 -Xmx1g -Xms256m
-XX:ParallelGCThreads=2 -Xmx1g -Xms256m
-XX:ParallelGCThreads=4 -Xmx1g -Xms256m
-XX:+UseConcMarkSweepGC -Xmx1g -Xms256m
-XX:ParallelGCThreads=1 -XX:+UseConcMarkSweepGC -Xmx1g -Xms256m
-XX:ParallelGCThreads=2 -XX:+UseConcMarkSweepGC -Xmx1g -Xms256m
-XX:ParallelGCThreads=4 -XX:+UseConcMarkSweepGC -Xmx1g -Xms256m
为了获得图表作为HTML页面的摘要,除了上面提到的GC设置外,还传递了以下VM参数:
-Djub.consumers=CONSOLE,H2 -Djub.db.file=.benchmarks
-Djub.customkey=[CUSTOM_KEY]
(其中[CUSTOM_KEY]
必须是唯一标识每个基准运行的字符串,例如defaultGC
或ParallelGCThreads=1
。它用作图表轴上的标签。)
下表总结了结果:
Run Custom key Timestamp test
1 defaultGC 2015-05-01 19:43:53.796 10.721
2 ParallelGCThreads=1 2015-05-01 19:51:07.79 8.770
3 ParallelGCThreads=2 2015-05-01 19:56:44.985 8.737
4 ParallelGCThreads=4 2015-05-01 20:01:30.071 10.415
5 UseConcMarkSweepGC 2015-05-01 20:03:54.474 2.683
6 UseCCMS,Threads=1 2015-05-01 20:10:48.504 3.856
7 UseCCMS,Threads=2 2015-05-01 20:12:58.624 3.861
8 UseCCMS,Threads=4 2015-05-01 20:13:58.94 2.701
系统信息:CPU:Intel Core 2 Quad Q9400,2.66 GHz,RAM:4.00 GB,操作系统:Windows 8.1 x64,JVM:1.8.0_05-b13。
(请注意,单个基准测试会输出更多详细信息,例如标准派生GC调用和时间;遗憾的是,此信息在摘要中不可用。)
<强>解释强>
如您所见,启用-XX:+UseConcMarkSweepGC
后会有巨大的性能提升。线程数不会对性能产生太大影响,并且如果有更多线程是有利的,则它取决于通用GC策略。默认GC似乎从两个或三个线程中获利,但如果使用四个线程,性能会变差。
相反,具有四个线程的 ConcurrentMarkSweep GC 比一个或两个线程更具性能。
所以一般来说,我们不能说更多的GC线程会让性能变差。
请注意,我不知道在使用默认GC或 ConcurrentMarkSweep GC 而未指定线程数时使用了多少个GC线程。
答案 1 :(得分:0)
https://community.oracle.com/thread/2191327
ParallelGCThreads设置线程数并可能为GC提供核心 将使用。
如果将其设置为8,则可以加快GC时间 它可能意味着您的所有其他应用程序必须停止或将要 与这些线程竞争。
拥有所有你的可能是不可取的 当任何JVM想要GC时,应用程序停止或减速。
因此,a 设置为2可能是您的最佳选择。你可能会发现3或4很好 对于你的使用模式(如果你的JVM通常是空闲的),否则我 建议,坚持2。