LinkedBlockingQueue的Java性能问题
这是我在stackoverflow上的第一篇文章…我希望有人可以帮助我
我使用Java 6 LinkedBlockingQueue
进行了很大的性能回归。 在第一个线程中,我生成了一些我推入队列的对象。在第二个线程中,我将这些对象拉出来。 当频繁调用LinkedBlockingQueue
的take()
方法时, take()
发生性能回归。 我监控整个程序, take()
方法总体上take()
了最多的时间。 吞吐量从~58Mb / s到0.9Mb / s ……
队列弹出并使用此类中的静态方法调用方法
public class C_myMessageQueue { private static final LinkedBlockingQueue x_queue = new LinkedBlockingQueue( 50000 ); /** * @param message * @throws InterruptedException * @throws NullPointerException */ public static void addMyMessage( C_myMessageObject message ) throws InterruptedException, NullPointerException { x_queue.put( message ); } /** * @return Die erste message der MesseageQueue * @throws InterruptedException */ public static C_myMessageObject getMyMessage() throws InterruptedException { return x_queue.take(); } }
如何调整take()
方法以达到至少25Mb / s,或者是否有一个我可以使用的类,它将在“队列”满或空时阻塞。
亲切的问候
巴特
PS:抱歉我的英语不好,我来自德国;)
您的生产者线程只是放置了比消费者消耗的元素更多的元素 ,因此队列最终达到其容量限制,因此生产者等待。
从现在起巩固我原来的答案我们基本上已经全面了解:
- 你通过执行非常快的
put()
来达到LinkedBlockingQueue
的固有吞吐量限制(每个队列都有一个),即使是连续的take()s
,没有进一步处理,也无法跟上。 (顺便说一下,这表明在这个结构中,无论如何,在你的JVM和机器上,put()s至少比读取的成本稍高一些)。 - 由于存在消费者锁定的特定锁定,因此放置更多的消费者线程可能无法提供帮助(如果您的消费者实际上正在进行某些处理并且限制了吞吐量,那么添加更多消费者将有所帮助。对于具有更好的队列实现不止一个消费者(或生产者),您可以尝试
SynchronousQueue
,ConcurrentLinkedQueue
和即将推出的jsr166y的TransferQueue
)。
一些建议:
- 尝试制作更粗粒度的对象,以便排队每个对象的开销与从生成线程卸载的实际工作相平衡(在您的情况下,似乎您为代表可忽略不计的工作量的对象创建了很多通信开销)
- 你也可以让生产者通过卸载一些消费工作来帮助消费者(当有工作要做时,没有多少时间等待。)
/在John W.正确地指出我的原始答案是误导之后更新
我通常建议不要在性能敏感的代码区域中使用LinkedBlockingQueue,使用ArrayBlockingQueue。 它将提供更好的垃圾收集配置文件,并且比LinkedBlockingQueue更加缓存友好。
尝试使用ArrayBlockingQueue并测量性能。
LinkedBlockingQueue的唯一优点是它可以是无界的,但这很少是你真正想要的。 如果您遇到消费者失败并且队列开始备份的情况,那么拥有有界队列会使系统优雅地降级,而不是冒险在队列无界时可能发生的OutOfMemoryErrors。
这里有几件事要尝试:
将LinkedBlockingQueue
替换为ArrayBlockingQueue
。 它没有悬空引用,因此在队列填满时表现更好。 具体来说,鉴于LinkedBlockingQueue的1.6实现,在队列实际变空之前,不会发生元素的完整GC。
如果生产者方面一直在执行消费者方面,请考虑使用drain
或drain
来执行“批量”采取操作。
或者,让队列获取消息对象的数组或列表。 生成器使用消息对象填充List或数组,每个put或take移动具有相同锁定开销的多个消息。 把它想象成一个秘书递给你一堆“当你外出”的消息,而不是一次一个地交给你。
如果不了解填充过程的内容,很难说会发生什么。
如果不经常调用addMyMessage
– 可能是因为应用程序的整个不同部分存在性能问题 – take
方法必须等待。
这样看起来像是罪魁祸首,但实际上它是你的应用程序的填充部分。
找到这篇关于队列大小和垃圾收集导致的性能问题的有趣post 。
您的应用程序可能会受到Java 6中与锁定相关的更改的影响,尤其是“偏向锁定”function。
尝试使用-XX:-UseBiasedLocking
开关禁用它,看看是否-XX:-UseBiasedLocking
。
有关详细信息,请参阅此处 : http : //java.sun.com/performance/reference/whitepapers/6_performance.html
不能肯定地告诉任何事情。 但您可以尝试更改BlockingQueue
实现(就像实验一样)。
您将初始容量设置为50k并使用LinkedBlockingQueue
。 尝试使用相同容量的ArrayBlockingQueue
,您也可以使用fair
参数。
如果从阻塞队列中放置和获取对象的原始性能开销是您的瓶颈(而不是缓慢的生产者/消费者问题 ),则可以通过批处理对象来获得巨大的性能提升:例如,不要使用或采用细粒度对象,您放置或采用粗粒度的对象列表。 这是一段代码:
ArrayBlockingQueue> Q = new ArrayBlockingQueue>(); // producer side List
批处理可以将您的性能提升一个数量级。