使用檔案預熱優化後,在進行記憶體對映後,會預先寫入資料到檔案中,並且將檔案內容載入到 page cache,當訊息寫入或者讀取的時候,可以直接命中 page cache,避免多次缺頁中斷。這個方法的作用就是檔案預熱。 • 提出問題 • 刨根問底
1)先探討第一個問題,關於缺頁中斷的原理,屬於計算機組成原理的範疇,這裡不展開詳細介紹,大概流程可以參照下面這張圖:
簡單解釋一下這個流程: • 程式通過CPU訪問虛擬地址VA,通過MMU找到對應的實體地址(主存),當記憶體頁在實體記憶體中沒有對應的頁幀或者存在但無對應的訪問許可權,在這種情況下,CPU就會報告一個缺頁的錯誤; • 實體記憶體中沒有對應的頁幀,需要CPU開啟磁碟裝置讀取到實體記憶體中,再讓MMU建立VA和PA的對映; • 缺頁對效能的影響,也得看具體情況,參照下圖:
◦ 從磁碟交換區中調入缺頁:百μ s至幾十ms ◦ 從磁碟檔案區中調入缺頁;幾十甚至幾百毫秒 ◦ 從磁碟緩衝區中調入缺頁:數百ns 如果不載入任何MappedFile資料至記憶體中的話,按照最壞的影響,1GB的CommitLog需要發生26w多次缺頁中斷。所以通過程式碼設計,減少缺頁的情況出現,會大大提升應用響應效率。 2)我們再來看第二個問題,為什麼迴圈次數是4K,為什麼往ByteBuffer中寫0? 傳統HDD扇區單位一直習慣於512Byte,有些檔案系統預設保留前63個扇區,也就是前512 * 63 / 1024 = 31.5KB,假設快閃記憶體Page和簇(OS讀寫基本單位)都大小為4KB,那麼一個Page對應著8個扇區,使用者資料將於第8個Page的第3.5KB位置開始寫入,導致之後的每一個簇都會跨兩個Page,讀寫處於超界處,這對於快閃記憶體會造成更多的讀損及讀寫開銷。 除了OS層的4K對齊至關重要以外,在檔案寫入過程中仍然需要關注4K對齊的問題。假設Page大小仍然為4KB,向一個空白檔案寫入5KB資料,此時需要2個Page來儲存資料,Page 1寫滿了4KB,而Page2只寫入1KB,當再次向檔案順序寫入資料時,需要將Page2資料預先讀出來,然後與新寫入資料在記憶體中合併後再寫入新的Page 3中,之前的Page 2則標記為 stale 等待被GC。這種帶來的開銷被稱為寫入放大WA(Write Amplification)。為了防止寫入放大的情形出現,我們會提前將Page空間,用0填充寫滿。
3)最後,我們再來看第三個問題,為什麼Thread.sleep程式碼塊,註釋上寫著:prevent gc? 這段程式表達的意思很容易理解,每執行1000次迴圈,執行一次Thread.sleep(0)語句。但背後的目的確沒那麼明顯。即Thread.sleep(0)可以讓執行緒進入 Safepoint,從而觸發GC。 這就得了解一下安全點(Safepoint),使用者程式執行時,並非在程式碼指令流的任意位置都能夠停頓下來,開始垃圾收集,而是強制要求必須執行到達安全點後才能夠暫停。意思就是在可數迴圈(Counted Loop)的情況下,HotSpot 虛擬機器器有一個優化,就是等迴圈結束之後,執行緒才會進入安全點。程式碼中int型別就屬於可數迴圈,當然Long型別屬於不可數迴圈。 總結一下,這段程式碼的目的就是,在預熱資料的時候,每寫入1000個位元組,讓該執行緒立即從執行階段進入就緒佇列,釋放CPU時間,可以讓作業系統切換其他執行緒來執行,比如GC執行緒的執行。這也側面的反映出系統設計者對資料響應效率的追求,通過人工介入GC頻率,防止出現超長時間GC情況的出現,影響瞬時的吞吐效率。
|