上一篇(傳送門(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