程序中有一个大列表,里面保存了300,000个长期存活的对象,不会被GC清除。那么,这些对象会不会对GC造成影响?比如,让每次GC都花费很多的时间?
因为GC时需要检查对所有对象的引用,所以我担心检查太多的对象,需要花很多时间。
写一个测试程序:
public class ListAddGc {
public static void main(String[] args) throws Exception { List<Long> list = new ArrayList<Long>(300000); for (int i = 0; i < 300000; i++) { long start = System.nanoTime(); list.add(start); long end = System.nanoTime(); long cost = end - start; } while (true) { new String("abc"); } }
}
该程序持有一个300000元素的list,然后通过不断生成新的String来触发GC操作。结果如下:
[GC 281K->146K(15872K), 0.0028494 secs]
[Full GC 146K->146K(15872K), 0.0096386 secs]
[GC 43689K->43598K(55000K), 0.0258150 secs]
[GC 48078K->43897K(55000K), 0.0048889 secs]
[GC 48377K->43897K(55000K), 0.0017662 secs]
[GC 63929K->43897K(72536K), 0.0003661 secs]
[GC 63929K->43897K(72536K), 0.0003503 secs]
[GC 63929K->43897K(72536K), 0.0000992 secs]
[GC 63929K->43897K(72536K), 0.0003927 secs]
[GC 63929K->43897K(72536K), 0.0003651 secs]
[GC 63929K->43897K(72536K), 0.0003592 secs]
[GC 63929K->43897K(72536K), 0.0003760 secs]
[GC 63929K->43897K(72536K), 0.0001119 secs]
[GC 63929K->43897K(72536K), 0.0004512 secs]
[GC 63929K->43897K(72536K), 0.0003288 secs]
可见每次普通GC花费的时间极小,可以忽略。通过观察后面的数据,发现很少发生Full GC。
于是我打开jvisualvm,通过它提供的“执行GC”按钮来触发Full GC。显示如下:
[Full GC 16199K->740K(72536K), 0.0374200 secs]
使用大约37ms。考虑到jvisualvm的影响,不使用jvisualvm,而修改代码,主动触发GC。代码如下:
for (int i = 0;; i++) {
new String("abc"); if (i % 1000 == 0) { System.gc(); }
}
显示如下:
[Full GC 43960K->146K(55000K), 0.0133591 secs]
[Full GC 551K->146K(72536K), 0.0114220 secs]
[Full GC 520K->146K(66904K), 0.0114949 secs]
[Full GC 406K->146K(46508K), 0.0112917 secs]
[Full GC 237K->146K(15936K), 0.0114735 secs]
[Full GC 237K->146K(15936K), 0.0104386 secs]
[Full GC 237K->146K(15936K), 0.0107348 secs]
[Full GC 237K->146K(15936K), 0.0120792 secs]
可见每次Full GC使用的时间约为10ms,可接触。考虑到只要设置了足够的Xmx,Full GC并不会经常发生。所以我认识使用一个大列表拥有对象,并不会对GC造成多大影响。
更多的讨论见我在stackoverflow上的提问: