java - 并发标记清除收集发生得不够频繁

标签 java garbage-collection concurrent-mark-sweep

我的应用程序在具有 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/

相关文章:

java - 为什么 JVM 不断进行 full gc,而老一代只有半满?

java - "final remark"中CMS的具体工作是什么?

java - 单元测试Android功能

java - 在录制的音频文件中查找立体声输出的延迟

java - 如何使用正则表达式删除字符串中不需要的空格和最后一个单词?

python - Python的垃圾回收能保证在任何情况下都回收循环引用对象吗?

java - 并发标记和清除算法

java - 在主类或主方法中声明数组有什么区别?

go - gctrace中与时间相关的字段

java - 显式设置分布式 GC 的间隔似乎不起作用