tomcat和netty中那种复用短生命周期的对象有啥优势?

yuyijq 2014-04-23
在tomcat里会缓存request对象,新来的request不再创建request,而是复用之前的request,然后只是将新的值set上去。
而在netty里我也找到差不多类似的做法:
netty 4的DefaultChannelHandlerContext里:
 private static WriteTask newInstance(
                DefaultChannelHandlerContext ctx, Object msg, int size, ChannelPromise promise) {
            WriteTask task = RECYCLER.get();
            init(task, ctx, msg, size, promise);
            return task;
        }

这个WriteTask基本上也是每次写出都用一个,也基本上是短生命周期的,这个RECYCLER是一个基于ThreadLocal的缓存。然后从缓存里拿到后设置值。

我的理解,这样的好处是可以节省new一次对象的时间开销。但是这样也把一个本来短生命周期对象变成了长生命周期对象,然后就有更大的几率提升到old gen,最后还会导致old gen对young gen对象的引用,虽然有card table的帮助,但这样也不是太好吧?
我没想到这种做法有什么特别的优势?求讲解~~
RednaxelaFX 2014-04-27
要说好处,这种object pooling对某些实现得不够好的GC可以有好处。

=============================================

有些分代式GC对晋升的对象的大小比较敏感,特别是old gen用segregated free list来管理空间的mark-sweep收集器。如果晋升的对象大小比较集中(例如说只有16字节、64字节和80字节三种),那old gen就不容易碎片化;反之,如果晋升的对象大小很分散,而且晋升对象的生命周期不够长,segregated free list很容易遇到碎片化的问题,进而使得可分配的连续空间变小,增加GC的频率。
这种情况下,如果能把那些“例外者”排除在外——要么不让它们晋升,要么保证它们晋升了就永远不会死掉,那就能让晋升对象的大小恢复到比较集中的状态,减少碎片化的影响。

HotSpot VM里,CMS就是一个对old gen做mark-sweep的并发收集器,当它遇到严重碎片化时就需要退到串行的mark-compact full GC。因而楼主提到的做法对CMS是可以有好处的。
但对那些总是对堆做compaction的收集器来说,这种object pooling就不见得有多少好处了。

=============================================

另一种情况是可能有JVM的GC不是分代式的。IBM Sovereign JVM(J9 JVM之前的一代)里的GC很长时间都不是分代式的。
分代式GC能有效“过滤”短命对象对GC的影响,而不分代的话短命对象还是会造成全局影响。这种情况下用object pooling也可以是有效的。
Global site tag (gtag.js) - Google Analytics