自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java Nio FileChannel堆內(nèi)堆外數(shù)據(jù)讀寫全流程分析及使用

開發(fā) 前端
CPU將page cache數(shù)據(jù)拷貝到DirectByteBuffer,因?yàn)閜age cache屬于內(nèi)核空間,JVM進(jìn)程無法直接尋址。這里是發(fā)生第二次數(shù)據(jù)拷貝。

背景

java nio中文件讀寫不管是普通文件讀寫,還是基于mmap實(shí)現(xiàn)零拷貝,都離不開FileChannel這個(gè)類。

隨便打開RocketMQ 源碼搜索FileChannel。

就可以看到使用頻率。

圖片圖片

kafka也是。

圖片圖片

所以在java中文件讀寫FileChannel尤為重用。

java文件讀寫全流程

圖片圖片

這里說的僅僅是FileChannel基于堆內(nèi)存(HeapByteBuffer)的文件讀寫。

如果是mmap或者堆外內(nèi)存,可能有些步驟會(huì)省略,相當(dāng)于有一些優(yōu)化。

  1. FileChannel調(diào)用read,將HeapByteBuffer拷貝到DirectByteBuffer。
  2. JVM在native層使用read系統(tǒng)調(diào)用進(jìn)行文件讀取, 這里需要進(jìn)行上下文切換,從用戶態(tài)進(jìn)入內(nèi)核態(tài)。
  3. JVM 進(jìn)程進(jìn)入虛擬文件系統(tǒng)層,查看文件數(shù)據(jù)再page cache是否緩存,如果有則直接從page cache讀取并返回到DirectByteBuffer。
  4. 如果請(qǐng)求文件數(shù)據(jù)不在page caceh,則進(jìn)入文件系統(tǒng)。通過塊驅(qū)動(dòng)設(shè)備進(jìn)行真正的IO,并進(jìn)行文件預(yù)讀,比如讀取的文件可能只有1-10,但是會(huì)將1-20都讀取。
  5. 磁盤控制器DMA將磁盤中的數(shù)據(jù)拷貝到page cache中。這里發(fā)生了一次數(shù)據(jù)拷貝(非CPU拷貝)。
  6. CPU將page cache數(shù)據(jù)拷貝到DirectByteBuffer,因?yàn)閜age cache屬于內(nèi)核空間,JVM進(jìn)程無法直接尋址。這里是發(fā)生第二次數(shù)據(jù)拷貝。
  7. JVM進(jìn)程從內(nèi)核態(tài)切換回用戶態(tài),這里如果使用的是堆內(nèi)存(HeapByteBuffer),實(shí)際還需要將堆外內(nèi)存DirectByteBuffer拷貝到堆內(nèi)存(HeapByteBuffer)。

FileChannel讀寫文件(非MMAP)

public static void main(String[] args) {
        String filename = "小奏技術(shù).txt";
        String content = "Hello, 小奏技術(shù).";
        // 寫入文件
        writeFile(filename, content);
        // 讀取文件
        System.out.println("Reading from file:");
        readFile(filename);
    }

    public static void writeFile(String filename, String content) {
        // 創(chuàng)建文件對(duì)象
        File file = new File(filename);
        // 確保文件存在
        if (!file.exists()) {
            try {
                boolean created = file.createNewFile();
                if (!created) {
                    System.err.println("Unable to create file: " + filename);
                    return;
                }
            } catch (Exception e) {
                System.err.println("An error occurred while creating the file: " + e.getMessage());
                return;
            }
        }
        // 使用FileChannel寫入文件
        try (RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
             FileChannel fileChannel = randomAccessFile.getChannel()) {

            ByteBuffer buffer = ByteBuffer.allocate(content.getBytes().length);
            buffer.put(content.getBytes());
            buffer.flip(); // 切換到讀模式
            while (buffer.hasRemaining()) {
                fileChannel.write(buffer);
            }
        } catch (Exception e) {
            System.err.println("An error occurred while writing to the file: " + e.getMessage());
        }
    }

    public static void readFile(String filename) {
        // 使用FileChannel讀取文件
        try (RandomAccessFile randomAccessFile = new RandomAccessFile(filename, "r");
             FileChannel fileChannel = randomAccessFile.getChannel()) {

            ByteBuffer buffer = ByteBuffer.allocate((int) fileChannel.size());

            while (fileChannel.read(buffer) > 0) {
                // Do nothing, just read
            }

            // 切換到讀模式
            buffer.flip(); 

           /* while (buffer.hasRemaining()) {

                System.out.print((char) buffer.get());
            }*/
            Charset charset = StandardCharsets.UTF_8; 
            String fileContent = charset.decode(buffer).toString();
            System.out.print(fileContent);

        } catch (Exception e) {
            System.err.println("An error occurred while reading the file: " + e.getMessage());
        }
    }

這里需要注意的一個(gè)細(xì)節(jié) 我們分配的內(nèi)存的方式是:

ByteBuffer.allocate()

這里我們可以進(jìn)入看看源碼:

圖片圖片

實(shí)際構(gòu)造的是HeapByteBuffer,也就是JVM的堆內(nèi)存。

如果我們使用:

ByteBuffer.allocateDirect()

圖片圖片

則構(gòu)造的是堆外內(nèi)存DirectByteBuffer。

HeapByteBuffer和DirectByteBuffer文件讀寫區(qū)別

我們看看FileChannel read方法:

圖片圖片

發(fā)現(xiàn)IO相關(guān)的處理被封裝在IOUtil,我們繼續(xù)看看IOUtil的write方法:

圖片圖片

可以看到如果是DirectBuffer則可以直接寫。如果是HeapByteBuffer則需要轉(zhuǎn)換為DirectByteBuffer。

圖片圖片

為什么要在DirectByteBuffer做一層轉(zhuǎn)換

主要是HeapByteBuffer受JVM管理,也就是會(huì)受到GC影響。如果在進(jìn)行native調(diào)用的時(shí)候發(fā)生了GC,會(huì)導(dǎo)致HeapByteBuffer的內(nèi)容出現(xiàn)錯(cuò)誤。具體詳細(xì)的說明可以看看這篇MappedByteBuffer VS FileChannel:從內(nèi)核層面對(duì)比兩者的性能差異。講解的非常清晰。

參考

  • MappedByteBuffer VS FileChannel:從內(nèi)核層面對(duì)比兩者的性能差異 
責(zé)任編輯:武曉燕 來源: 小奏技術(shù)
相關(guān)推薦

2022-02-14 15:07:48

進(jìn)程FileChanne線程

2017-01-11 14:02:32

JVM源碼內(nèi)存

2020-03-06 16:08:46

堆結(jié)構(gòu)堆排序應(yīng)用

2018-04-17 14:41:41

Java堆內(nèi)存溢出

2021-10-08 11:45:33

內(nèi)存HeapByteBuf堆內(nèi)

2022-06-15 16:04:13

Java編程語言

2021-02-26 20:55:56

JavaNIO隨機(jī)

2022-07-03 20:31:59

JVMJava虛擬機(jī)

2024-11-15 09:14:23

JDK4NIO函數(shù)

2015-08-06 14:54:50

JavaScript分析工具OneHeap

2022-12-26 14:41:38

Linux內(nèi)存

2012-03-31 10:49:18

ibmdw

2022-04-29 08:05:06

內(nèi)存堆外GC

2022-09-21 08:39:52

堆外內(nèi)存泄露內(nèi)存分布

2022-04-15 07:51:12

off-heap堆外內(nèi)存JVM

2019-02-26 14:33:22

JVM內(nèi)存虛擬機(jī)

2018-06-15 08:43:33

Java堆外內(nèi)存

2020-07-13 09:16:04

Java集合

2009-06-08 22:01:03

Java堆Java棧區(qū)別

2020-05-12 10:00:14

緩存算法贈(zèng)源碼
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)