在看
内存管理术语表的时候偶然发现了”
Pig in the Python(注:有点像中文里的贪心不足蛇吞象)”的定义,于是便有了这篇文章。表面上看,这个术语说的是GC不停地将大对象从一个分代提升到另一个分代的情景。这么做就好比巨蟒整个吞食掉它的猎物,以至于它在消化的时候都没办法移动了。
在接下来的这24个小时里我的头脑中充斥着这个令人窒息的巨蟒的画面,挥之不去。正如精神病医生所说的,消除恐惧最好的方法就是说出来。于是便有了这篇文章。不过接下的故事我们要讲的不是蟒蛇,而是GC的调优。我对天发誓。
大家都知道GC暂停很容易造成性能瓶颈。现代JVM在发布的时候都自带了高级的垃圾回收器,不过从我的使用经验来看,要找出某个应用最优的配置真是难上加难。手动调优或许仍有一线希望,但是你得了解GC算法的确切机制才行。关于这点,本文倒是会对你有所帮助,下面我会通过一个例子来讲解JVM配置的一个小的改动是如何影响到你的应用程序的吞吐量的。
示例
我们用来演示GC对吞吐量产生影响的应用只是一个简单的程序。它包含两个线程:
PigEater - 它会模仿巨蟒不停吞食大肥猪的过程。代码是通过往java.util.List中添加 32MB字节来实现这点的,每次吞食完后会睡眠100ms。
PigDigester - 它模拟异步消化的过程。实现消化的代码只是将猪的列表置为空。由于这是个很累的过程,因此每次清除完引用后这个线程都会睡眠2000ms。
两个线程都会在一个while循环中运行,不停地吃了消化直到蛇吃饱为止。这大概得吃掉5000头猪。
package eu.plumbr.demo;
public class PigInThePython {
static volatile List pigs = new ArrayList();
static volatile int pigsEaten = 0;
static final int ENOUGH_PIGS = 5000;
public static void main(String[] args) throws InterruptedException {
new PigEater().start();
new PigDigester().start();
}
static class PigEater extends Thread {
@Override
public void run() {
while (true) {
pigs.add(new byte[32 * 1024 * 1024]); //32MB per pig
if (pigsEaten > ENOUGH_PIGS) return;
takeANap(100);
}
}
}
static class PigDigester extends Thread {
@Override
public void run() {
long start = System.currentTimeMillis();
while (true) {
takeANap(2000);
pigsEaten+=pigs.size();
pigs = new ArrayList();
if (pigsEaten > ENOUGH_PIGS) {
System.out.format("Digested %d pigs in %d ms.%n",pigsEaten, System.currentTimeMillis()-start);
return;
}
}
}
}
static void takeANap(int ms) {
try {
Thread.sleep(ms);
} catch (Exception e) {
e.printStackTrace();
}
}
}
现在我们将这个系统的吞吐量定义为“每秒可以消化的猪的头数”。考虑到每100ms就会有猪被塞到这条蟒蛇里,我们可以看到这个系统理论上的最大吞吐量可以达到10头/秒。
GC配置示例
我们来看下使用两个不同的配置系统的表现分别是什么样的。不管是哪个配置,应用都运行在一台拥有双核,8GB内存的Mac(OS X10.9.3)上。
第一个配置:
4G的堆(-Xms4g -Xmx4g)
使用CMS来清理年老代(-XX:+UseConcMarkSweepGC)使用并行回收器清理新生代(-XX:+UseParNewGC)
将堆的12.5%(-Xmn512m)分配给新生代,并将Eden区和Survivor区的大小限制为一样的。
第二个配置则略有不同:
2G的堆(-Xms2g -Xms2g)
新生代和年老代都使用Parellel GC(-XX:+UseParallelGC)
将堆的75%分配给新生代(-Xmn 1536m)
现在是该下注的时候了,哪个配置的表现会更好一些(就是每秒能吃多少猪,还记得吧)?那些把筹码放到第一个配置上的家伙,你们一定会失望的。结果正好相反:
第一个配置(大堆,大的年老代,CMS GC)每秒能吞食8.2头猪
第二个配置(小堆,大的新生代,Parellel GC)每秒可以吞食9.2头猪
现在我们来客观地看待一下这个结果。分配的资源少了2倍但吞吐量提升了12%。这和常识正好相反,因此有必要进一步分析下到底发生了什么。
分析GC的结果
原因其实并不复杂,你只要仔细看一下运行测试的时候GC在干什么就能发现答案了。这个你可以自己选择要使用的工具。在jstat的帮助下我发现了背后的秘密,命令大概是这样的:
<quote>
jstat -gc -t -h20 PID 1s
</quote>
通过分析数据,我注意到配置1经历了1129次GC周期(YGCT_FGCT),总共花了63.723秒:
Timestamp S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT
594.0 174720.0 174720.0 163844.1 0.0 174848.0 131074.1 3670016.0 2621693.5 21248.0 2580.9 1006 63.182 116 0.236 63.419
595.0 174720.0 174720.0 163842.1 0.0 174848.0 65538.0 3670016.0 3047677.9 21248.0 2580.9 1008 63.310 117 0.236 63.546
596.1 174720.0 174720.0 98308.0 163842.1 174848.0 163844.2 3670016.0 491772.9 21248.0 2580.9 1010 63.354 118 0.240 63.595
597.0 174720.0 174720.0 0.0 163840.1 174848.0 131074.1 3670016.0 688380.1 21248.0 2580.9 1011 63.482 118 0.240 63.723
第二个配置一共暂停了168次(YGCT+FGCT),只花了11.409秒。
Timestamp S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT
539.3 164352.0 164352.0 0.0 0.0 1211904.0 98306.0 524288.0 164352.2 21504.0 2579.2 27 2.969 141 8.441 11.409
540.3 164352.0 164352.0 0.0 0.0 1211904.0 425986.2 524288.0 164352.2 21504.0 2579.2 27 2.969 141 8.441 11.409
541.4 164352.0 164352.0 0.0 0.0 1211904.0 720900.4 524288.0 164352.2 21504.0 2579.2 27 2.969 141 8.441 11.409
542.3 164352.0 164352.0 0.0 0.0 1211904.0 1015812.6 524288.0 164352.2 21504.0 2579.2 27 2.969 141 8.441 11.409
考虑到两种情况下的工作量是等同的,因此——在这个吃猪的实验中当GC没有发现长期存活的对象时,它能更快地清理掉垃圾对象。而采用第一个配置的话,GC运行的频率大概会是6到7倍之多,而总的暂停时间则是5至6倍。
说这个故事有两个目的。第一个也是最主要的一个,我希望把这条抽风的蟒蛇赶紧从我的脑海里赶出去。另一个更明显的收获就是——GC调优是个很需要技巧的经验活,它需要你对底层的这些概念了如指掌。尽管本文中用到的这个只是很平常的一个应用,但选择的不同结果也会对你的吞吐量和容量规划产生很大的影响。在现实生活中的应用里面,这里的区别则会更为巨大。因此,就看你如何抉择了,你可以去掌握这些概念,或者,只关注你日常的工作就好了,让Plumbr来找出你所需要的最合适的
GC配置吧。
更多文章请移步我的个人博客:
Java译站
原创文章转载请注明出处:
http://it.deepinmind.com
英文原文链接
分享到:
相关推荐
Java中的垃圾回收器(Garbage ...总之,优化Java应用的GC对吞吐量的影响是一项复杂但重要的任务,需要深入理解GC的工作原理,并结合具体应用的特点进行调整。通过合理的配置和实践,可以有效地提升系统的整体性能。
不同的应用程序对 GC 的需求不同,一般的 web 应用对吞吐量要求不高,而实时性强的应用程序对 GC 的需求则更高。 JvmGC 收集器的选择 JvmGC 收集器的选择取决于应用程序的特点和需求。如果应用程序运行在单处理器...
- Parallel GC:多线程版本的Serial GC,提高了吞吐量。 - CMS(Concurrent Mark Sweep)GC:并行标记,低延迟,适用于响应时间敏感的应用。 - G1(Garbage-First)GC:新一代的垃圾收集器,目标是达到可预测的...
- **Throughput(吞吐量)**: 即系统用于处理非GC的时间比例。 - 如果系统花费在GC上的时间为1%,当系统跨越32个处理器时,系统的吞吐量将减少超过20%。 - 如果系统花费在GC上的时间为10%,当系统跨越32个处理器时...
此外,右侧的统计面板提供了内存使用量、暂停时间、吞吐量等关键指标,帮助开发者评估应用的性能。 对于复杂的GC问题,GCViewer还支持过滤和比较功能。你可以根据特定条件筛选GC事件,或者同时加载多个日志文件进行...
与传统的UART(通用异步接收器发送器)端口相比,PL2303GC通过利用USB批量传输模式和大数据缓冲区,能够实现更高的吞吐量。 PL2303GC的灵活波特率发生器也可以编程为生成1 bps至12 Mbps之间的任何速率。
不同的GC策略会影响应用的性能,例如,吞吐量优先、响应时间优先或者平衡策略。 Java提供了多种垃圾收集器,如Serial GC、Parallel GC、CMS(Concurrent Mark Sweep)GC、G1 GC以及ZGC(Z Garbage Collector)等。...
通过对gc.log的分析,我们可以了解系统的内存使用状况,调整参数以满足特定的性能需求,如降低暂停时间或提高吞吐量。同时,利用合适的工具进行监控和诊断,可以更好地管理和优化我们的Java系统。
调优GC主要是为了平衡吞吐量和响应时间。可以通过调整以下参数实现: - `-Xms` 和 `-Xmx`:设置堆的最小和最大大小。 - `-XX:NewRatio`:年轻代与老年代的比例。 - `-XX:SurvivorRatio`:新生代中Eden区与Survivor区...
垃圾回收性能问题的表现形式多样,最直观的就是长时间的GC暂停和低吞吐量。诊断这类问题时,首先要确认是否选择了合适的GC策略,其次要检查堆内存的大小设置是否恰当,最后还要分析具体的应用行为和垃圾回收日志,...
KPIs是衡量系统性能的关键指标,包括吞吐量(Throughput)和延迟(Latency)。报告中提到吞吐量为99.255%,这意味着程序在执行非垃圾收集任务时的效率。延迟方面,分为年轻代GC时间和老年代GC时间,分别为10.9毫秒...
GCViewer由Stefan Zeiger开发,主要功能是解析JVM的日志文件,并以图表形式展示垃圾收集的详细信息,包括GC的执行时间、内存区域的变化、吞吐量等关键指标。这对于诊断内存泄漏、优化内存配置以及理解垃圾收集算法的...
在Java环境中,尤其是大型企业级应用如WAS中,正确配置GC参数至关重要,因为它直接影响到服务的响应时间、吞吐量以及系统的稳定性。gc日志提供了关于JVM内存使用情况的详细信息,包括对象的创建、存活和销毁过程。...
根据C4的研究论文,该算法在执行持续的工作负载时能够提供出色的吞吐量和暂停时间数据。具体来说,在各种基准测试中,C4展示了其在保持高分配速率的同时,仍然能够维持良好的响应时间和较低的暂停时间。 #### 五、...
4. G1 GC:新一代的垃圾回收器,目标是达到低延迟,通过分区技术来平衡吞吐量和暂停时间。 5. ZGC:最新一代的低延迟GC,目标是在大内存环境下实现极低的暂停时间。 四、垃圾回收的过程 GC主要包括三个阶段:标记、...
例如,吞吐量优先的场景下可以选择并行垃圾回收器(Parallel GC),而响应时间优先的情况下则更适合使用并发标记清扫垃圾回收器(CMS)或G1垃圾回收器。 3. **监控和分析**:使用JVisualVM、Visual GC等工具监控...
- 基于区域的垃圾收集器,旨在提供可预测的暂停时间和高吞吐量。 #### 六、永久代(PermGen)与元空间(Metaspace) 1. **永久代** - 在Java 8之前,类的元数据存储在永久代中。 - 当永久代空间不足时,会触发Full ...
它设计的目标是在处理多TB(Terabyte)堆内存时,最大暂停时间不超过10毫秒,并且最大吞吐量减少不超过15%,使得内存管理对应用性能的影响降至最低。ZGC的设计理念是易于调整,以适应各种规模的应用。 在GC...
1. **选择合适的GC策略**:根据应用的需求(如低延迟、高吞吐量)选择最适合的GC算法。 2. **调整内存大小**:合理设定堆内存大小,避免OutOfMemoryError。 3. **监控GC行为**:使用JConsole、VisualVM或JMX等工具...