LinkedBlockingQueue 的 Java 性能问题
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2644688/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java performance problem with LinkedBlockingQueue
提问by lofthouses
this is my first post on stackoverflow... I hope someone can help me
这是我在 stackoverflow 上的第一篇文章...我希望有人能帮助我
I have a big performance regression with Java 6 LinkedBlockingQueue
.
In the first thread i generate some objects which i push in to the queue
In the second thread i pull these objects out. The performance regression occurs when the take()
method of the LinkedBlockingQueue
is called frequently.
I monitored the whole program and the take()
method claimed the most time overall.
And the throughput goes from ~58Mb/s to 0.9Mb/s...
我对 Java 6 有很大的性能回归LinkedBlockingQueue
。在第一个线程中,我生成了一些对象,并将它们推入队列在第二个线程中,我将这些对象拉出。当 的take()
方法LinkedBlockingQueue
被频繁调用时,就会出现性能回归。我监控了整个程序,该take()
方法总体上占用了最多的时间。吞吐量从 ~58Mb/s 到 0.9Mb/s...
the queue pop and take methods ar called with a static method from this class
使用此类中的静态方法调用队列 pop 和 take 方法
public class C_myMessageQueue {
private static final LinkedBlockingQueue<C_myMessageObject> x_queue = new LinkedBlockingQueue<C_myMessageObject>( 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();
}
}
how can I tune the take()
method to accomplish at least 25Mb/s, or is there a other class I can use which will block when the "queue" is full or empty.
我如何调整take()
方法以实现至少 25Mb/s,或者是否有其他类我可以使用它会在“队列”已满或空时阻塞。
kind regards
亲切的问候
Bart
巴特
P.S.: sorry for my bad english, I'm from Germany ;)
PS:抱歉我的英语不好,我来自德国;)
采纳答案by Dimitris Andreou
Your producer thread simply puts more elements than the consumer consumes, so the queue eventually hits its capacity limit, thus the producer waits.
您的生产者线程只是放入比消费者消耗更多的元素,因此队列最终达到其容量限制,因此生产者等待。
Consolidating my original answer since now we have basically the full picture:
巩固我的原始答案,因为现在我们基本上有了全貌:
- You hit the inherent throughput limit of the
LinkedBlockingQueue
(every queue has one) by doing extremely fastput()
s, where even continualtake()s
, with zero further processing, cannot keep up. (By the way this shows that in this structure, on your JVM and machine anyway, put()s are at least slightly more costly than the reads). - Since there is a particular lock that consumers lock, putting more consumer threads could not possibly help (if your consumer was actually doing some processing and that was bounding the throughput, then adding more consumers would help. There are better queue implementations for a scenario with more than one consumers (or producers), you could try
SynchronousQueue
,ConcurrentLinkedQueue
, and the upcomingTransferQueue
of jsr166y).
LinkedBlockingQueue
通过执行极快的put()
s,您达到了(每个队列都有一个)的固有吞吐量限制,其中即使 continualtake()s
进一步处理为零,也无法跟上。(顺便说一下,这表明在这种结构中,无论如何,在您的 JVM 和机器上,put() 至少比读取成本略高)。- 由于消费者锁定了一个特定的锁,因此放置更多的消费者线程可能无济于事(如果您的消费者实际上正在执行一些处理并且限制了吞吐量,那么添加更多消费者会有所帮助。对于具有以下功能的场景,有更好的队列实现不止一个消费者(或生产者),你可以尝试
SynchronousQueue
,ConcurrentLinkedQueue
以及即将TransferQueue
jsr166y的)。
Some suggestions:
一些建议:
- Try to make more coarse-grained objects, so that the overhead of queueing each is balanced with the actual work that is offloaded from the producing thread (in your case, it seems you create much communication overhead for objects that represent negligible amounts of work)
- You could also have the producer help the consumer by offloading some consuming work (not much point in waiting idly when there is work to be done).
- 尝试制作更粗粒度的对象,以便将每个对象排队的开销与从生产线程卸载的实际工作相平衡(在您的情况下,您似乎为代表可忽略的工作量的对象创建了很多通信开销)
- 您还可以让生产者通过卸载一些消耗性工作来帮助消费者(在有工作要做时无所事事地等待没有多大意义)。
/updated after John W. rightly pointed out my original answer was misleading
/在 John W. 正确指出我最初的答案具有误导性之后更新
回答by Daniel Rikowski
It's hard to say what happens without knowing something about the filling process.
如果不了解灌装过程,很难说会发生什么。
If addMyMessage
is called less frequently - perhaps because of a performance problem in a whole different part of your application - the take
method has to wait.
如果 addMyMessage
调用频率较低 - 可能是因为应用程序的整个不同部分的性能问题 - 该take
方法必须等待。
That way it looks like take
is the culprit, but actually it's the filling part of your application.
这样看起来take
是罪魁祸首,但实际上它是应用程序的填充部分。
回答by Daniel Rikowski
It could be possible that your application is affected by the locking related changes in Java 6, especially the "biased locking" feature.
您的应用程序可能会受到 Java 6 中与锁定相关的更改的影响,尤其是“偏向锁定”功能。
Try disabling it by using the -XX:-UseBiasedLocking
switch and see if that makes a difference.
尝试使用-XX:-UseBiasedLocking
开关禁用它,看看是否有所不同。
See this for further information: http://java.sun.com/performance/reference/whitepapers/6_performance.html
有关更多信息,请参阅:http: //java.sun.com/performance/reference/whitepapers/6_performance.html
回答by JRL
Found this interesting postabout performance problems due to queue size and garbage collection.
回答by Roman
Can't tell anything for sure. But you can try to change BlockingQueue
implementation (just as an experiment).
不能确定什么。但是你可以尝试改变BlockingQueue
实现(就像一个实验)。
You set initial capacity 50k and use LinkedBlockingQueue
. Try ArrayBlockingQueue
with the same capacity, you can also play with fair
parameter.
您将初始容量设置为 50k 并使用LinkedBlockingQueue
. ArrayBlockingQueue
用同样的容量试试,你也可以用fair
参数玩。
回答by Michael Barker
I would generally recommend not using a LinkedBlockingQueue in a performance sensitive area of code, use an ArrayBlockingQueue. It will giving a much nicer garbage collection profile and is more cache friendly than the LinkedBlockingQueue.
我通常建议不要在性能敏感的代码区域使用 LinkedBlockingQueue,而使用 ArrayBlockingQueue。它将提供更好的垃圾收集配置文件,并且比 LinkedBlockingQueue 对缓存更友好。
Try the ArrayBlockingQueue and measure the performance.
尝试使用 ArrayBlockingQueue 并测量性能。
The only advantage of the LinkedBlockingQueue is that it can be unbounded, however this is rarely what you actually want. If you have a case where a consumer fails and queues start backing up, having bounded queues allows the system to degrade gracefully rather risk OutOfMemoryErrors that may occur if queues are unbounded.
LinkedBlockingQueue 的唯一优点是它可以是无界的,但这很少是您真正想要的。如果您遇到消费者失败并且队列开始备份的情况,则有界队列允许系统优雅地降级,而不是冒可能在队列无界时发生 OutOfMemoryErrors 的风险。
回答by Devon_C_Miller
Here are a couple of things to try:
这里有几件事可以尝试:
Replace the LinkedBlockingQueue
with an ArrayBlockingQueue
. It has no dangling references and so is better behaved when the queue fills up. Specifically, given the 1.6 implementation of LinkedBlockingQueue, full GC of the elements will not happen until the queue actually becomes empty.
更换LinkedBlockingQueue
用的ArrayBlockingQueue
。它没有悬空引用,因此在队列填满时表现更好。具体来说,考虑到 LinkedBlockingQueue 的 1.6 实现,在队列实际变空之前不会发生元素的完全 GC。
If the producer side is consistently out performing the consumer side, consider using drain
or drainTo
to perform a "bulk" take operation.
如果生产者端始终优于消费者端,请考虑使用drain
或drainTo
执行“批量”获取操作。
Alternatively, have the queue take arrays or Lists of message objects. The the producer fills a List or array with message objects and each put or take moves multiple messages with the same locking overhead. Think of it as a secretary handing you a stack of "While you were out" messages vs. handing them to you one at a time.
或者,让队列采用消息对象的数组或列表。生产者用消息对象填充列表或数组,每个放置或获取以相同的锁定开销移动多个消息。把它想象成一个秘书递给你一堆“当你外出时”的信息,而不是一次递给你一个。
回答by mac7
If the raw performance overhead for putting and taking object from your blockingqueues is your bottleneck (and not the slow-producer/consumer problem), you can get huge performance improvements with batching of objects: For instance, instead of putting or taking fine-grained objects, you put or take coarse-grained lists of objects. Here is a code snippet:
如果从阻塞队列中放入和取出对象的原始性能开销是你的瓶颈(而不是生产者/消费者缓慢的问题),你可以通过对象批处理获得巨大的性能改进:例如,而不是放入或取出细粒度对象,您放置或获取对象的粗粒度列表。这是一个代码片段:
ArrayBlockingQueue<List<Object>> Q = new ArrayBlockingQueue<List<Object>>();
// producer side
List<Object> l = new ArrayList<Object>();
for (int i=0; i<100; i++) {
l.add(i); // your initialization here
}
Q.put(l);
// consumer side
List<Object> l2 = Q.take();
// do something
Batching can boost your performance by an order of magnitude.
批处理可以将您的性能提高一个数量级。