您需要确保了解当前的情况和期望的结果,而不是因为对应用程序的随机部分进行调整而产生了诱惑。 通常,只需执行以下过程即可:
- 陈述您的绩效目标
- 运行测试
- 测量
- 与目标比较
- 进行更改并返回运行测试
重要的是,可以设定和衡量目标的三个维度都与性能调整有关。 这些目标包括延迟,吞吐量和容量,理解,我建议您阅读垃圾收集手册中的相应章节。
让我们看看我们如何开始研究设定和实现这些目标的实际情况。 为此,让我们看一个示例代码:
//imports skipped for brevity
public class Producer implements Runnable {
private static ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2);
private Deque<byte[]> deque;
private int objectSize;
private int queueSize;
public Producer(int objectSize, int ttl) {
this.deque = new ArrayDeque<byte[]>();
this.objectSize = objectSize;
this.queueSize = ttl * 1000;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
deque.add(new byte[objectSize]);
if (deque.size() > queueSize) {
deque.poll();
}
}
}
public static void main(String[] args) throws InterruptedException {
executorService.scheduleAtFixedRate(new Producer(200 * 1024 * 1024 / 1000, 5), 0, 100, TimeUnit.MILLISECONDS);
executorService.scheduleAtFixedRate(new Producer(50 * 1024 * 1024 / 1000, 120), 0, 100, TimeUnit.MILLISECONDS);
TimeUnit.MINUTES.sleep(10);
executorService.shutdownNow();
}
}
该代码将提交两个作业,每100毫秒运行一次。 每个作业都模拟具有特定寿命的对象:它创建对象,让它们离开预定的时间,然后忘记它们,从而使GC回收内存。
在使用以下参数打开GC日志记录的情况下运行示例时
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps
我们开始立即在日志文件中看到GC的影响,类似于以下内容:
2015-06-04T13:34:16.119-0200: 1.723: [GC (Allocation Failure) [PSYoungGen: 114016K->73191K(234496K)] 421540K->421269K(745984K), 0.0858176 secs] [Times: user=0.04 sys=0.06, real=0.09 secs]
2015-06-04T13:34:16.738-0200: 2.342: [GC (Allocation Failure) [PSYoungGen: 234462K->93677K(254976K)] 582540K->593275K(766464K), 0.2357086 secs] [Times: user=0.11 sys=0.14, real=0.24 secs]
2015-06-04T13:34:16.974-0200: 2.578: [Full GC (Ergonomics) [PSYoungGen: 93677K->70109K(254976K)] [ParOldGen: 499597K->511230K(761856K)] 593275K->581339K(1016832K), [Metaspace: 2936K->2936K(1056768K)], 0.0713174 secs] [Times: user=0.21 sys=0.02, real=0.07 secs]
根据日志中的信息,我们可以着眼于三个不同的目标来改善情况
- 确保最坏情况下的GC暂停不超过预定阈值
- 确保停止应用程序线程的总时间不超过预定阈值
- 降低基础架构成本,同时确保我们仍然可以实现合理的延迟和/或吞吐量目标。
为此,上面的代码在三种不同的配置上运行了10分钟,导致下表总结了三种截然不同的结果:
堆 | GC算法 | 有用的工作 | 最长的停顿 |
---|---|---|---|
-Xmx12g | -XX:+ UseConcMarkSweepGC | 89.8% | 560毫秒 |
-Xmx12g | -XX:+ UseParallelGC | 91.5% | 1,104毫秒 |
-Xmx8g | -XX:+ UseConcMarkSweepGC | 66.3% | 1,610毫秒 |
实验使用不同的GC算法和不同的堆大小运行相同的代码,以测量有关延迟和吞吐量的垃圾收集暂停的持续时间。 有关实验的详细信息和结果的解释,请参见《 垃圾收集手册》 。 请看一下手册中的示例,了解配置的简单更改如何使示例在延迟,容量吞吐量方面完全不同。
请注意,为了使示例尽可能简单,仅更改了有限数量的输入参数,例如,实验未在不同数量的内核或不同的堆布局上进行测试。
翻译自: https://www.javacodegeeks.com/2015/06/gc-tuning-in-practice.html