我的应用程序在具有 16 个处理器和 64 GB RAM 的服务器上正确运行。我有多个进程,并且尝试将进程的最大堆限制为 8 GB。
我的问题是,我有某种形式的生产者-消费者模式,我必须限制生产速率,否则我将耗尽内存,因为老一代的垃圾收集很少发生。
- 当我监控我的应用程序时,我可以看到运行 4 小时后,我在 ParNEW 中花费了 7 分钟,在 ConcurrentMarkSweep 中花费了 0.775 秒。
- 我的堆占用上升到 6GB 左右,然后下降到 1GB,然后慢慢开始上升到 6GB,然后再次下降。周期大约为 10 分钟
通过JVisualVM,我可以看到90%的内存占用是CMS Old Gen提供的,我如何强制并发标记扫描运行得更频繁一些?
@PeterLawrey 评论非常相关,因为我的应用程序运行在应用程序服务器的顶部,该服务器专为事件驱动的处理和数据分区而设计,例如 Terracotta 或 Coherence。底层实现很可能包括一个用于事件处理的排队系统。
我的问题是,限制堆大小并不是一个解决方案,因为正如我所经历的那样,应用程序并没有进行更频繁的垃圾回收,而是耗尽了内存。
最佳答案
I have to limit the producing rate or I will run out of memory
如果您使用无界队列,就会发生这种情况。如果你的生产者超过了消费者的速率,队列就会无限制地增长,直到内存耗尽。限制生产者可以确保队列保持在合理的大小。
解决这个问题的一个简单方法是当队列太长时延迟生产者。
private final BlockingQueue<Task> tasks = new ArrayBlockingQueue<Task>(1000);
// the producer slows when the queue length gets too long.
tasks.offer(newTask, 1, TimeUnit.HOURS);
这将确保队列永远不会变得太长,但不会不必要地减慢生产者的速度。
顺便说一句:与链接队列相比,使用 ArrayBlockingQueue 还可以减少产生的垃圾量。
如果你确实需要一个无界队列,你可以使用我编写的一个库,但它的级别相对较低。 Java Chronicle生产者可以比消费者领先于主内存的大小。它仅受磁盘空间大小的限制。
关于java - 并发标记清除收集发生得不够频繁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12461788/