Java 內存映射讀取文件_Java內存映射 大文件輕松處理|chu

前言

內存映射文件(Memory-mapped File),指的是將一段虛擬內存逐字節映射于一個文件,使得應用程序處理文件如同訪問主內存(但在真正使用到這些數據前卻不會消耗物理內存,也不會有讀寫磁盤的操作),這要比直接文件讀寫快幾個數量級。

稍微解釋一下虛擬內存(很明顯,不是物理內存),它是計算機系統內存管理的一種技術。像施了妖法一樣使得應用程序認為它擁有連續的可用的內存,實際上呢,它通常是被分隔成多個物理內存的碎片,還有部分暫時存儲在外部磁盤存儲器上,在需要時進行數據交換。

內存映射文件主要的用處是增加 I/O 性能,特別是針對大文件。對于小文件,內存映射文件反而會導致碎片空間的浪費,因為內存映射總是要對齊頁邊界,最小單位是 4 KiB,一個 5 KiB 的文件將會映射占用 8 KiB 內存,也就會浪費 3 KiB 內存。

java.nio 包使得內存映射變得非常簡單,其中的核心類叫做 MappedByteBuffer,字面意思為映射的字節緩沖區。

01、使用 MappedByteBuffer 讀取文件

假設現在有一個文件,名叫 cmower.txt,里面的內容是:

沉默王二,一個有趣的程序員

PS:哎,改不了王婆賣瓜自賣自夸這個臭毛病了,因為文章被盜得都怕了。

這個文件放在 /resource 目錄下,我們可以通過下面的方法獲取到它:

ClassLoader classLoader = Cmower.class.getClassLoader(); Path path = Paths.get(classLoader.getResource("cmower.txt").getPath());

Path 既可以表示一個目錄,也可以表示一個文件,就像 File 那樣――當然了,Path 是用來取代 File 的。

然后,從文件中獲取一個 channel(通道,對磁盤文件的一種抽象)。

FileChannel fileChannel = FileChannel.open(path);

緊接著,調用 FileChannel 類的 map 方法從 channel 中獲取 MappedByteBuffer,此類擴展了 ByteBuffer――提供了一些內存映射文件的基本操作方法。

MappedByteBuffer mappedByteBuffer = fileChannel.map(mode, position, size);

稍微解釋一下 map 方法的三個參數。

1)mode 為文件映射模式,分為三種:MapMode.READ_ONLY(只讀),任何試圖修改緩沖區的操作將導致拋出 ReadOnlyBufferException 異常。

MapMode.READ_WRITE(讀/寫),任何對緩沖區的更改都會在某個時刻寫入文件中。需要注意的是,其他映射同一個文件的程序可能不能立即看到這些修改,多個程序同時進行文件映射的行為依賴于操作系統。

MapMode.PRIVATE(私有), 對緩沖區的更改不會被寫入到該文件,任何修改對這個緩沖區來說都是私有的。

2)position 為文件映射時的起始位置。

3)size 為要映射的區域的大小,必須是非負數,不得大于Integer.MAX_VALUE。

一旦把文件映射到內存緩沖區,我們就可以把里面的數據讀入到 CharBuffer 中并打印出來。具體的代碼示例如下。

CharBuffer charBuffer = null; ClassLoader classLoader = Cmower.class.getClassLoader(); Path path = Paths.get(classLoader.getResource("cmower.txt").getPath()); try (FileChannel fileChannel = FileChannel.open(path)) { MappedByteBuffer mappedByteBuffer = fileChannel.map(MapMode.READ_ONLY, 0, fileChannel.size()); if (mappedByteBuffer != null) { charBuffer = Charset.forName("UTF-8").decode(mappedByteBuffer); } System.out.println(charBuffer.toString()); } catch (IOException e) { e.printStackTrace(); }

由于 decode() 方法的參數是 MappedByteBuffer,這就意味著我們是從內存中而不是磁盤中讀入的文件內容,所以速度會非常快。

02、使用 MappedByteBuffer 寫入文件

假設現在要把下面的內容寫入到一個文件,名叫 cmower1.txt。

沉默王二,《Web全棧開發進階之路》作者

這個文件還沒有創建,計劃放在項目的 classpath 目錄下。

Path path = Paths.get("cmower1.txt");

具體位置見下圖所示。

20190926_5d8c3e310f4d5.png

然后,創建文件的通道。

FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)

仍然使用的 open 方法,不過增加了 3 個參數,前 2 個很好理解,表示文件可讀(READ)、可寫(WRITE);第 3 個參數 TRUNCATE_EXISTING 的意思是如果文件已經存在,并且文件已經打開將要進行 WRITE 操作,則其長度被截斷為 0。

緊接著,仍然調用 FileChannel 類的 map 方法從 channel 中獲取 MappedByteBuffer。

MappedByteBuffer mappedByteBuffer = fileChannel.map(MapMode.READ_WRITE, 0, 1024);

這一次,我們把模式調整為 MapMode.READ_WRITE,并且指定文件大小為 1024,即 1KB 的大小。然后使用 MappedByteBuffer 中的 put() 方法將 CharBuffer 的內容保存到文件中。具體的代碼示例如下。

CharBuffer charBuffer = CharBuffer.wrap("沉默王二,《Web全棧開發進階之路》作者"); Path path = Paths.get("cmower1.txt"); try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) { MappedByteBuffer mappedByteBuffer = fileChannel.map(MapMode.READ_WRITE, 0, 1024); if (mappedByteBuffer != null) { mappedByteBuffer.put(Charset.forName("UTF-8").encode(charBuffer)); } } catch (IOException e) { e.printStackTrace(); }

可以打開 cmower1.txt 查看一下內容,確認預期的內容有沒有寫入成功。

03、MappedByteBuffer 的遺憾

據說,在 Java 中使用 MappedByteBuffer 是一件非常麻煩并且痛苦的事,主要表現有:

1)一次 map 的大小最好限制在 1.5G 左右,重復 map 會增加虛擬內存回收和重新分配的壓力。也就是說,如果文件大小不確定的話,就不太友好。

2)虛擬內存由操作系統來決定什么時候刷新到磁盤,這個時間不太容易被程序控制。

3)MappedByteBuffer 的回收方式比較詭異。

再次強調,這三種說法都是據說,我暫時能力有限,也不能確定這種說法的準確性,很遺憾。

04、比較文件操作的處理時間

嗨,朋友,閱讀完以上的內容之后,我想你一定對內存映射文件有了大致的了解。但我相信,如果你是一名負責任的程序員,你一定還想知道:內存映射文件的讀取速度究竟有多快。

為了得出結論,我叫了另外三名競賽的選手:InputStream(普通輸入流)、BufferedInputStream(帶緩沖的輸入流)、RandomAccessFile(隨機訪問文件)。

讀取的對象是加勒比海盜4驚濤怪浪.mkv,大小為 1.71G。

1)普通輸入流

public static void inputStream(Path filename) { try (InputStream is = Files.newInputStream(filename)) { int c; while((c = is.read()) != -1) { } } catch (IOException e) { e.printStackTrace(); } }

2)帶緩沖的輸入流

public static void bufferedInputStream(Path filename) { try (InputStream is = new BufferedInputStream(Files.newInputStream(filename))) { int c; while((c = is.read()) != -1) { } } catch (IOException e) { e.printStackTrace(); } }

3)隨機訪問文件

public static void randomAccessFile(Path filename) { try (RandomAccessFile randomAccessFile = new RandomAccessFile(filename.toFile(), "r")) { for (long i = 0; i < randomAccessFile.length(); i++) { randomAccessFile.seek(i); } } catch (IOException e) { e.printStackTrace(); } }

4)內存映射文件

public static void mappedFile(Path filename) { try (FileChannel fileChannel = FileChannel.open(filename)) { long size = fileChannel.size(); MappedByteBuffer mappedByteBuffer = fileChannel.map(MapMode.READ_ONLY, 0, size); for (int i = 0; i < size; i++) { mappedByteBuffer.get(i); } } catch (IOException e) { e.printStackTrace(); } }

測試程序也很簡單,大致如下:

long start = System.currentTimeMillis(); bufferedInputStream(Paths.get("jialebi.mkv")); long end = System.currentTimeMillis(); System.out.println(end-start);

四名選手的結果如下表所示。方法時間普通輸入流龜速,沒有耐心等出結果

隨機訪問文件龜速,沒有耐心等下去

帶緩沖的輸入流29966

內存映射文件914

普通輸入流和隨機訪問文件都慢得要命,真的是龜速,我沒有耐心等待出結果;帶緩沖的輸入流的表現還不錯,但相比內存映射文件就遜色多了。由此得出的結論就是:內存映射文件,上G大文件輕松處理。

05、最后

本篇文章主要介紹了 Java 的內存映射文件,MappedByteBuffer 是其靈魂,讀取速度快如火箭。另外,所有這些示例和代碼片段都可以在 GitHub 上找到――這是一個 Maven 項目,所以它很容易導入和運行。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持華域聯盟。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/445800.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/445800.shtml
英文地址,請注明出處:http://en.pswp.cn/news/445800.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

LeetCode - Easy - 118. Pascal‘s Triangle

Topic Array Description https://leetcode.com/problems/pascals-triangle/ Given a non-negative integer numRows, generate the first numRows of Pascal’s triangle. In Pascal’s triangle, each number is the sum of the two numbers directly above it. Example…

LeetCode - Easy - 119. Pascal‘s Triangle II

Topic Array Description https://leetcode.com/problems/pascals-triangle-ii/ Given an integer rowIndex, return the rowIndexth row of the Pascal’s triangle. Notice that the row index starts from 0. In Pascal’s triangle, each number is the sum of the tw…

jenv java_mac 上使用jenv 管理的多個java 版本

由于服務器是java1.7&#xff0c; mac上是1.8&#xff0c;因此mac編譯的java代碼會在服務器上報錯。因此&#xff0c;需要修改mac上java版本&#xff0c;自己折騰了很久&#xff0c;放棄&#xff0c;決定使用jenv 管理&#xff01; 結果是非常方便使用步驟&#xff1a;1、安裝 …

mysql 源碼 緩存_MySQL源碼:MYSQL存儲過程/函數的分析原理及緩存機制

前言&#xff1a;我個人認為&#xff0c;有關MYSQL存儲過程/函數在MYSQL中的實現比較粗糙&#xff0c;可擴展性不夠好&#xff0c;其實現的耦合性太高&#xff0c;所以主要講一些它的原理方面的內容&#xff0c;但有可能在某些方面理解不夠好或者有些不正確的地方&#xff0c;歡…

如何單元測試Java的private方法

問題 Java類中private方法通常只能被其所屬類的調用&#xff0c;其他類只能望而卻步&#xff0c;單元測試private方法也就一籌莫展。 嘗試解法&#xff1a; 在測試時&#xff0c;手動將private改為public&#xff0c;測試完后再將其改回。將測試方法寫進private方法的所屬類…

圖論與java_算法筆記_150:圖論之雙連通及橋的應用(Java)

1 問題描述DescriptionIn order to get from one of the F (1 < F < 5,000) grazing fields (which are numbered 1..F) to another field, Bessie and the rest of the herd are forced to cross near the Tree of Rotten Apples. The cows are now tired of often bein…

如何用JUnit單元測試List

問題 JUnit測試List時差強人意。 解法 引入依賴 hamcrest-library包含許多有用方法來測試List數據類型。 <dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version>&l…

java數據包解析_請教http請求數據包如何解析 重組

該樓層疑似違規已被系統折疊 隱藏此樓查看此樓下面是我捕獲到的請求報文片段dst_ip:/121.52.228.134ack:trueack_num:3064957366date:POST /messagebroker/amf HTTP/1.1Host: s16.xxhzw.game.yy.comUser-Agent: Mozilla/5.0 (Windows NT 5.1; rv:13.0) Gecko/20100101 Firefox/…

webqq java_WebQQ登錄詳解

第二次登錄請求方式:POST地址:http://d.web2.qq.com/channel/login2POST正文:r%7B%22status%22%3A%22online%22%2C%22ptwebqq%22%3A%22{0}%22%2C%22passwd_sig%22%3A%22%22%2C%22clientid%22%3A%22{1}%22%2C%22  psessionid%22%3Anull%7D&clientid{2}&psessionidnull…

LeetCode - Easy - 155. Min Stack

Topic StackDesign Description https://leetcode.com/problems/min-stack/ Design a stack that supports push, pop, top, and retrieving the minimum element in constant time. push(x) – Push element x onto stack.pop() – Removes the element on top of the st…

java judgefilecode_VScode出現無法打開“X”: 找不到文件(file:///XXXX) 的解決辦法

如標題&#xff0c;被這個問題整了好長時間了&#xff0c;調試的時候如果有語法錯誤只能顯示相應的的行數&#xff0c;沒有辦法定位到出錯的行數上。(由于用處不是很大并且沒有找到解決辦法&#xff0c;所以就一直放著沒管23333)直到最近看到一位大佬的解決辦(重寫正則表達式)法…

LeetCode - Easy - 169. Majority Element

Topic ArrayDivide and ConquerBit Manipulation Description https://leetcode.com/problems/majority-element/ Given an array of size n, find the majority element. The majority element is the element that appears more than ? n/2 ? times. You may assume t…

java 靜態方法 異常_java空指針異常與靜態方法

從一道經典面試題說起&#xff0c;public class HaHa {public static void haha(){System.out.println("haha");}public static void main(String[] args){((HaHa)null).haha();}}打印結果 haha。這段題考查兩點知識&#xff0c;java的空指針異常和靜態方法。1&#…

java中的asList_Java中的Arrays.asList()方法

Arrays.asList()返回一個List&#xff0c;但是這種情況下&#xff0c;其底層的實現是一個final數組&#xff0c;因此不能調整其尺寸如下代碼片段&#xff1a;package chapter11.t1;import java.util.*;public class AddingGroups {public static void main(String[] args) {Lis…

java控制面板作用_Java

1. JAVA 的特性和優勢(1) Java的核心優勢 跨平臺/可移植性(2) 其他特性 安全性&#xff1b;面對對象&#xff1b;簡單性&#xff1b;高性能&#xff1b;分布式&#xff1b;多線程&#xff1b;健壯性&#xff1b;① 強大的生態系統(3) Java與C的關系 Java是C的簡化版(C—)2. JAV…

java es 數據批量導入_ElasticSearch—Java批量導入導出

網上找了很多&#xff0c;我的es是2.3.5版本&#xff0c;網上的客戶端最少都是5.x版本&#xff0c;所以沒有能用的。自己整合了一下 2.3.5版本的。pom文件&#xff1a;org.elasticsearchelasticsearch2.3.5com.alibabafastjson1.1.35org.apache.commonscommons-io1.3.2org.apac…

java原始模型模式_java設計模式--原始模型模式

簡介原始模型模式屬于對象的創建模式。通過一個原型對象來指明要創建對象的類型&#xff0c;然后用復制原型對象的方法來創建出更多同類型的對象。Java所有的類都是從java.lang.Object類繼承來的&#xff0c;Object類提供clone()方法對對象進行復制。一般調用clone()方法需要滿…

Windows的命令行窗口運行Python時,如何清屏?

問題 如標題 解法 import os os.system("cls")參考 python實現清屏

手寫文字識別java_java 手寫文字圖片識別提取 百度API

package org.fh.util;import org.json.JSONObject;import java.io.BufferedReader;import java.io.InputStreamReader;import java.net.HttpURLConnection;import java.net.URL;import java.util.List;import java.util.Map;/*** 說明&#xff1a;獲取文字識別token類* from&am…

LeetCode - Easy - 191. Number of 1 Bits

Topic Bit Manipulation Description https://leetcode.com/problems/number-of-1-bits/ Write a function that takes an unsigned integer and returns the number of ‘1’ bits it has (also known as the Hamming weight). Note: Note that in some languages such …