計算機內存管理
原文鏈接 https://www.cnblogs.com/guozp/p/10470431.html
MMC:CPU的內存管理單元。
物理內存:即內存條的內存空間。
虛擬內存:計算機系統內存管理的一種技術。它使得應用程序認為它擁有連續的可用的內存(一個連續完整的地址空間),而實際上,它通常是被分隔成多個物理內存碎片,還有部分暫時存儲在外部磁盤存儲器上,在需要時進行數據交換。
頁面文件:操作系統反映構建並使用虛擬內存的硬盤空間大小而創建的文件,在windows下,即pagefile.sys文件,其存在意味着物理內存被占滿后,將暫時不用的數據移動到硬盤上。
缺頁中斷:當程序試圖訪問已映射在虛擬地址空間中但未被加載至物理內存的一個分頁時,由MMC發出的中斷。如果操作系統判斷此次訪問是有效的,則嘗試將相關的頁從虛擬內存文件中載入物理內存。
MappedByteBuffer介紹
MappedByteBuffer 是Java NIO中引入的一種硬盤物理文件和內存映射方式,當物理文件較大時,采用MappedByteBuffer,讀寫性能較高,其內部的核心實現是DirectByteBuffer(JVM 堆外直接物理內存)。
JVM 進程通過內存映射方式加載的物理文件並不會耗費同等大小的物理內存。當應用程序訪問數據時,程序通過虛擬地址尋址對應的內存頁,如果物理內存中不存在對應頁,MMU則會產生缺頁中斷異常,CPU嘗試從系統Swap分區中查找,如仍不存在,則會直接從硬盤中物理文件中讀取。
傳統的基於文件流的方式讀取文件方式是系統指令調用,文件數據首先會被讀取到進程的內核空間的緩沖區,而后復制到進程的用戶空間,這個過程中存在兩次數據拷貝;而內存映射方式讀取文件的方式,也是系統指令調用,在產生缺頁中斷后,CPU直接從磁盤文件load數據到進程的用戶空間,只有一次數據拷貝。
FileChannel提供了map方法把磁盤文件映射到虛擬內存,通常情況可以映射整個文件,如果文件比較大,可以進行分段映射。
內存映像文件訪問的方式,共三種:
a) MapMode.READ_ONLY:只讀,試圖修改得到的緩沖區將導致拋出異常。
b) MapMode.READ_WRITE:讀/寫,對得到的緩沖區的更改最終將寫入文件;但該更改對映射到同一文件的其他程序不一定是可見的。
c) MapMode.PRIVATE:私用,可讀可寫,但是修改的內容不會寫入文件,只是buffer自身的改變。
MappedByteBuffer在處理大文件時的確性能很高,但也存在一些問題,其所對應的內存使用的是JVM堆外內存,JVM young gc和CMS gc並不能觸發回收MappedByteBuffer對應的內存,只有full gc(stop the world的方式)可以使其回收內存,堆外直接內存會根據自己的情況(當需要新分配直接內存時,如果所剩堆外內存空間不夠,第一次產生OutOfMemoryError時)來觸發 System.gc(),此處有坑,若JVM配置了參數-XX:DisableExplicitGC,System.gc()將不會觸發full gc,最終導致內存泄漏。而且觸發其內存回收的時間點是不確定的。Java api文檔中標注:
A mapped byte buffer and the file mapping that it represents remain valid until the buffer itself is garbage-collected.
在應用程序頻繁使用堆外內存時,還可以通過-XX:MaxDirectMemorySize來指定最大的堆外內存大小,當使用達到了閾值的時候將調用System.gc來做一次full gc,以此來回收掉游離狀態的堆外內存。
因此,在使用堆外內存高性能的福利的同時,及時的回收掉廢棄掉的內存是十分關鍵的。
性能分析
從代碼層面上看,從硬盤上將文件讀入內存,都要經過文件系統進行數據拷貝,並且數據拷貝操作是由文件系統和硬件驅動實現的,理論上來說,拷貝數據的效率是一樣的。
但是通過內存映射的方法訪問硬盤上的文件,效率要比read和write系統調用高,這是為什么?
read()是系統調用,首先將文件從硬盤拷貝到內核空間的一個緩沖區,再將這些數據拷貝到用戶空間,實際上進行了兩次數據拷貝;
map()也是系統調用,但沒有進行數據拷貝,當缺頁中斷發生時,直接將文件從硬盤拷貝到用戶空間,只進行了一次數據拷貝。
所以,采用內存映射的讀寫效率要比傳統的read/write性能高。
總結
MappedByteBuffer使用虛擬內存,因此分配(map)的內存大小不受JVM的-Xmx參數限制,但是也是有大小限制的。
如果當文件超出1.5G限制時,可以通過position參數重新map文件后面的內容。
MappedByteBuffer在處理大文件時的確性能很高,但也存在一些問題,如內存占用、文件關閉不確定,被其打開的文件只有在垃圾回收的才會被關閉,而且這個時間點是不確定的。
javadoc中也提到:A mapped byte buffer and the file mapping that it represents remain* valid until the buffer itself is garbage-collected.
原文鏈接 https://www.cnblogs.com/guozp/p/10470431.html