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也可以是有效的。 |