RocketMQ消息引擎關(guān)于內(nèi)存預(yù)熱

上一篇(傳送門(mén))文章把RocketMQ的內(nèi)存預(yù)熱提到了一部分,但是代碼層面具體的實(shí)現(xiàn)還會(huì)沒(méi)搞徹底,今天咨詢(xún)了不少人,本篇做一下總結(jié)。

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();
}

該程序有三個(gè)點(diǎn)是比較有意思的:

  • 如果刷盤(pán)方式為同步,則往每個(gè)pageCache寫(xiě)入一個(gè)字節(jié)0,并且當(dāng)寫(xiě)入頁(yè)數(shù)大于等于(1024 / 4 * 16)時(shí)刷盤(pán);
  • Thead.sleep(0) 備注為防止GC;它為何可以防止GC?這里為何要防止GC?
  • mlock()方法包括mlock以及madvise;

為何要往每個(gè)pageCache也寫(xiě)入一個(gè)byte?

使用mmap建立內(nèi)存映射后,僅僅只是建立了進(jìn)程虛擬內(nèi)存地址與物理內(nèi)存地址之間的映射關(guān)系,但是并沒(méi)有將pageCache加載至內(nèi)存,寫(xiě)數(shù)據(jù)時(shí)如果沒(méi)有命中寫(xiě)pageCache則發(fā)出缺頁(yè)中斷,為了解決該問(wèn)題則往每個(gè)pageCache寫(xiě)入一個(gè)byte,確保在預(yù)熱時(shí)就已經(jīng)將pageCache加載至內(nèi)存中。

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

咨詢(xún)了一些朋友,得到了一些結(jié)論,但是似乎并沒(méi)有很明確的解答,本次僅作為筆記記錄。
首先,sleep(0)與yield()都是有剩余時(shí)間片讓權(quán)的能力。但是sleep(0)的語(yǔ)義其實(shí)是陷入阻塞,進(jìn)入就緒隊(duì)列,從而被動(dòng)讓出剩余時(shí)間片,而yield()則主動(dòng)建議操作系統(tǒng)讓出本次剩余時(shí)間片,主動(dòng)進(jìn)入就緒隊(duì)列。

除此之外,sleep(0)的語(yǔ)義上是將剩余時(shí)間 片讓給優(yōu)先級(jí)相同或者優(yōu)先級(jí)更高的線程,而yield()則沒(méi)有,僅僅是進(jìn)入了就緒隊(duì)列。

在JVM中,GC線程的級(jí)別是非常低的,讓出該剩余時(shí)間片給其余的更高級(jí)別的線程可以從反面起到防止GC的目的。不管是sleep()還是yield()在JDK都沒(méi)有具體的實(shí)現(xiàn),所以在不同的操作系統(tǒng)層次上顯示出來(lái)的結(jié)果可能不盡相同。具體結(jié)果后續(xù)有遇到合適的答案再做進(jìn)一步解釋。

此處為何要防止GC?

沒(méi)有看懂具體的原因,不知道意欲何為,如果有了解的童鞋請(qǐng)解答一下。

mlock與madvise

請(qǐng)關(guān)注上一篇文章:傳送門(mén)

PS: 關(guān)于prevent-gc的問(wèn)題,代碼已經(jīng)被刪除了哈,具體參照https://github.com/apache/rocketmq/issues/522

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容