美文网首页
RocketMQ消息引擎关于内存预热

RocketMQ消息引擎关于内存预热

作者: 丑人林宗己 | 来源:发表于2019-06-24 22:50 被阅读0次

上一篇(传送门)文章把RocketMQ的内存预热提到了一部分,但是代码层面具体的实现还会没搞彻底,今天咨询了不少人,本篇做一下总结。

public void warmMappedFile(FlushDiskType type, int pages) {
    long beginTime = System.currentTimeMillis();
    ByteBuffer byteBuffer = this.mappedByteBuffer.slice();
    int flush = 0;
    long time = System.currentTimeMillis();
    for (int i = 0, j = 0; i < this.fileSize; i += MappedFile.OS_PAGE_SIZE, j++) {
        byteBuffer.put(i, (byte) 0);
        // force flush when flush disk type is sync
        if (type == FlushDiskType.SYNC_FLUSH) {
            if ((i / OS_PAGE_SIZE) - (flush / OS_PAGE_SIZE) >= pages) {
                flush = i;
                mappedByteBuffer.force();
            }
        }

        // prevent gc
        if (j % 1000 == 0) {
            log.info("j={}, costTime={}", j, System.currentTimeMillis() - time);
            time = System.currentTimeMillis();
            try {
                Thread.sleep(0);
            } catch (InterruptedException e) {
                log.error("Interrupted", e);
            }
        }
    }

    // force flush when prepare load finished
    if (type == FlushDiskType.SYNC_FLUSH) {
        log.info("mapped file warm-up done, force to disk, mappedFile={}, costTime={}",
            this.getFileName(), System.currentTimeMillis() - beginTime);
        mappedByteBuffer.force();
    }
    log.info("mapped file warm-up done. mappedFile={}, costTime={}", this.getFileName(),
        System.currentTimeMillis() - beginTime);

    this.mlock();
}

该程序有三个点是比较有意思的:

  • 如果刷盘方式为同步,则往每个pageCache写入一个字节0,并且当写入页数大于等于(1024 / 4 * 16)时刷盘;
  • Thead.sleep(0) 备注为防止GC;它为何可以防止GC?这里为何要防止GC?
  • mlock()方法包括mlock以及madvise;

为何要往每个pageCache也写入一个byte?

使用mmap建立内存映射后,仅仅只是建立了进程虚拟内存地址与物理内存地址之间的映射关系,但是并没有将pageCache加载至内存,写数据时如果没有命中写pageCache则发出缺页中断,为了解决该问题则往每个pageCache写入一个byte,确保在预热时就已经将pageCache加载至内存中。

Thread.sleep(0)为何可以防止GC?

咨询了一些朋友,得到了一些结论,但是似乎并没有很明确的解答,本次仅作为笔记记录。
首先,sleep(0)与yield()都是有剩余时间片让权的能力。但是sleep(0)的语义其实是陷入阻塞,进入就绪队列,从而被动让出剩余时间片,而yield()则主动建议操作系统让出本次剩余时间片,主动进入就绪队列。

除此之外,sleep(0)的语义上是将剩余时间 片让给优先级相同或者优先级更高的线程,而yield()则没有,仅仅是进入了就绪队列。

在JVM中,GC线程的级别是非常低的,让出该剩余时间片给其余的更高级别的线程可以从反面起到防止GC的目的。不管是sleep()还是yield()在JDK都没有具体的实现,所以在不同的操作系统层次上显示出来的结果可能不尽相同。具体结果后续有遇到合适的答案再做进一步解释。

此处为何要防止GC?

没有看懂具体的原因,不知道意欲何为,如果有了解的童鞋请解答一下。

mlock与madvise

请关注上一篇文章:传送门

PS: 关于prevent-gc的问题,代码已经被删除了哈,具体参照https://github.com/apache/rocketmq/issues/522

相关文章

网友评论

      本文标题:RocketMQ消息引擎关于内存预热

      本文链接:https://www.haomeiwen.com/subject/yjbaqctx.html