Java中垃圾回收器GC对吞吐量的影响测试
在看内存管理术语表的时候偶然发现了”PiginthePython(注:有点像中文里的贪心不足蛇吞象)”的定义,于是便有了这篇文章。表面上看,这个术语说的是GC不停地将大对象从一个分代提升到另一个分代的情景。这么做就好比巨蟒整个吞食掉它的猎物,以至于它在消化的时候都没办法移动了。
在接下来的这24个小时里我的头脑中充斥着这个令人窒息的巨蟒的画面,挥之不去。正如精神病医生所说的,消除恐惧最好的方法就是说出来。于是便有了这篇文章。不过接下的故事我们要讲的不是蟒蛇,而是GC的调优。我对天发誓。
大家都知道GC暂停很容易造成性能瓶颈。现代JVM在发布的时候都自带了高级的垃圾回收器,不过从我的使用经验来看,要找出某个应用最优的配置真是难上加难。手动调优或许仍有一线希望,但是你得了解GC算法的确切机制才行。关于这点,本文倒是会对你有所帮助,下面我会通过一个例子来讲解JVM配置的一个小的改动是如何影响到你的应用程序的吞吐量的。
示例
我们用来演示GC对吞吐量产生影响的应用只是一个简单的程序。它包含两个线程:
PigEater–它会模仿巨蟒不停吞食大肥猪的过程。代码是通过往java.util.List中添加32MB字节来实现这点的,每次吞食完后会睡眠100ms。
PigDigester–它模拟异步消化的过程。实现消化的代码只是将猪的列表置为空。由于这是个很累的过程,因此每次清除完引用后这个线程都会睡眠2000ms。
两个线程都会在一个while循环中运行,不停地吃了消化直到蛇吃饱为止。这大概得吃掉5000头猪。
packageeu.plumbr.demo;
publicclassPigInThePython{ staticvolatileListpigs=newArrayList(); staticvolatileintpigsEaten=0; staticfinalintENOUGH_PIGS=5000;
publicstaticvoidmain(String[]args)throwsInterruptedException{ newPigEater().start(); newPigDigester().start(); }
staticclassPigEaterextendsThread{
@Override publicvoidrun(){ while(true){ pigs.add(newbyte[32*1024*1024]);//32MBperpig if(pigsEaten>ENOUGH_PIGS)return; takeANap(100); } } }
staticclassPigDigesterextendsThread{ @Override publicvoidrun(){ longstart=System.currentTimeMillis();
while(true){ takeANap(2000); pigsEaten+=pigs.size(); pigs=newArrayList(); if(pigsEaten>ENOUGH_PIGS) { System.out.format("Digested%dpigsin%dms.%n",pigsEaten,System.currentTimeMillis()-start); return; } } } }
staticvoidtakeANap(intms){ try{ Thread.sleep(ms); }catch(Exceptione){ e.printStackTrace(); } } }