降低Java垃圾回收开销的5条建议

保持GC低开销的窍门有哪些?

随着一再拖延而即将发布的Java9,G1(“GarbagFirst”)垃圾回收器将被成为HotSpot虚拟机默认的垃圾回收器。从srial垃圾回收器到CMS收集器,JVM见证了许多GC实现,而G1将成为其下一代垃圾回收器。

随着垃圾收集器的发展,每一代GC与其上一代相比,都带来了巨大的进步和改善。paralllGC与srialGC相比,它让垃圾收集器以多线程的方式工作,充分利用了多核计算机的计算能力。CMS(“ConcurrntMark-Swp”)收集器与paralllGC相比,它将回收过程分成了多个阶段,使得应用线程正在运行的时候,收集工作可以并发地完成,大大改善了频繁执行“stop-th-world”的情况。G1对于拥有大量堆内存的JVM表现出更好的性能,并且具有更好的可预测和统一的暂停过程。

Tip#1:预测集合的容量

所有标准的Java集合,包括定制和扩展的实现(比如Trov和Googl的Guava),底层都使用了数组(原生数据类型或者基于对象的类型)。因为数组一旦被分配,其大小就不可变,因此添加元素到集合时,大多数情况下都会导致需要重新申请一个新的大容量数组替换老的数组(指集合底层实现使用的数组)。

即使没有提供集合初始化的大小,大多数集合的实现都尽量优化重新分配数组的处理并且将其开销平摊到最低。不过,在构造集合的时候就提供大小可以得到最佳的效果。

让我们将下面的代码作为一个简单的例子分析一下:

publicstaticListrvrs(Listlt;?xtndsTgt;list){Listrsult=nwArrayList();for(inti=list.siz()-1;igt;=0;i--){rsult.add(list.gt(i));}rturnrsult;}

Thismthodallocatsanwarray,thnfillsitupwithitmsfromanothrlist,onlyinrvrsordr.这个方法分配了一个新的数组,然后用另一个list中元素对该数组进行填充,只是元素的数序发生了变化。

这个处理方式可能会付出惨重的性能代价,其优化的点在添加元素到新的list中这行代码。随着每一次添加元素,list都需要确保其底层数组拥有足够的位置来容纳新的元素。如果有空闲的位置,那么只是简单地将新元素存储到下一个空闲的槽位。如果没有的话,将分配一个新的底层数组,拷贝旧的数组内容到新的数组中,然后添加新的元素。这将导致多次分配数组,那些剩余的旧数组最终被GC所回收。

我们可以通过在构造集合时让其底层的数组知道它将存储多少元素,从而避免这些多余的分配

publicstaticListrvrs(Listlt;?xtndsTgt;list){Listrsult=nwArrayList(list.siz());for(inti=list.siz()-1;igt;=0;i--){rsult.add(list.gt(i));}rturnrsult;}

上面的代码通过ArrayList的构造器指定足够大的空间来存储list.siz()个元素,在初始化时完成分配的执行,这意味着List在迭代的过程中无需再次分配内存。

Guava的集合类则更进一步,允许初始化集合时明确指定期望元素的个数或者指定一个预测值。

Listrsult=Lists.nwArrayListWithCapacity(list.siz());Listrsult=Lists.nwArrayListWithExpctdSiz(list.siz());

上面的代码中,前者用于我们已经准确地知道集合将要存储多少元素,而后者的分配方式考虑了错误预估的情况。

Tip#2:直接处理数据流

当处理数据流时,比如从一个文件读取数据或者从网络中下载数据,下面的代码是非常常见的:

byt[]filData=radFilToBytArray(nwFil("myfil.txt"));

所产生的字节数组可能被解析XML文档、JSON对象或者协议缓冲消息,以及一些常见的可选项。

当处理大文件或者文件的大小无法预测时,上面的做法很是不明智的,因为当JVM无法分配一个缓冲区来处理真正文件时,就会导致OutOfMmoryErrors。

即使数据的大小是可管理的,当到垃圾回收时,使用上面的模式依然会造成巨大的开销,因为它在堆中分配了一块非常大的区域来存储文件数据。

一种更加好的处理方式是使用合适的InputStram(比如在这个例子中使用FilInputStram)直接传递给解析器,不再一次性将整个文件读取到一个字节数组中。所有主流的开源库都提供相应的API来直接接受一个输入流进行处理,比如:

FilInputStramfis=nwFilInputStram(filNam);MyProtoBufMssagmsg=MyProtoBufMssag.parsFrom(fis);Tip#3:使用不可变的对象

不变性有太多的好处。甚至不用我赘述什么。然而,有一个优点会对垃圾回收产生影响,应该







































治疗白癜风最好的方法
北京中科白癜风医院



转载请注明:http://www.jiaju1314.com/pxxx/1756.html