jvm源码分析之FileChannel.map()方法

map()方法

直接将文件从硬盘拷贝到用户空间,只进行了一次数据拷贝。

mmap的内存开销是不受JVM heap控制的,也就是堆外内存。mmap的文件数据被从磁盘load进物理内存,RES增加。

/**
     * Maps a region of this channel's file directly into memory.
     *
     * <p> A region of a file may be mapped into memory in one of three modes:
     * </p>
     *
     * <ul>
     *
     *   <li><p> <i>Read-only:</i> Any attempt to modify the resulting buffer
     *   will cause a {@link java.nio.ReadOnlyBufferException} to be thrown.
     *   ({@link MapMode#READ_ONLY MapMode.READ_ONLY}) </p></li>
     *
     *   <li><p> <i>Read/write:</i> Changes made to the resulting buffer will
     *   eventually be propagated to the file; they may or may not be made
     *   visible to other programs that have mapped the same file.  ({@link
     *   MapMode#READ_WRITE MapMode.READ_WRITE}) </p></li>
     *
     *   <li><p> <i>Private:</i> Changes made to the resulting buffer will not
     *   be propagated to the file and will not be visible to other programs
     *   that have mapped the same file; instead, they will cause private
     *   copies of the modified portions of the buffer to be created.  ({@link
     *   MapMode#PRIVATE MapMode.PRIVATE}) </p></li>
     *
     * </ul>
     *
     * <p> For a read-only mapping, this channel must have been opened for
     * reading; for a read/write or private mapping, this channel must have
     * been opened for both reading and writing.
     *
     * <p> The {@link MappedByteBuffer <i>mapped byte buffer</i>}
     * returned by this method will have a position of zero and a limit and
     * capacity of <tt>size</tt>; its mark will be undefined.  The buffer and
     * the mapping that it represents will remain valid until the buffer itself
     * is garbage-collected.
     *
     * <p> A mapping, once established, is not dependent upon the file channel
     * that was used to create it.  Closing the channel, in particular, has no
     * effect upon the validity o
<think>我们正在讨论FileChannel.transferTo()方法,它提示"cannot bereferenced froma staticcontext"错误。这个错误通常是因为在静态方法中使用了非静态的成员变量或方法,或者直接尝试在静态上下文中调用非静态方法。在FileChannel的案例中,transferTo()是一个实例方法,必须通过一个FileChannel对象来调用。错误示例: ```javapublic classMain{public staticvoid main(String[]args){//错误:在静态方法main中,不能直接使用非静态方法FileChannel.transferTo(...);//错误调用} }```正确做法:先获取FileChannel实例,然后通过该实例调用transferTo。下面提供一个正确的代码示例,展示如何正确使用FileChannel.transferTo()方法:</think>### 解决 `FileChannel.transferTo()` 静态调用错误 `cannot be referenced from a static context` 错误表明你尝试在静态上下文中调用非静态方法 `transferTo()`。`FileChannel.transferTo()` 是一个**实例方法**,必须通过具体的 `FileChannel` 对象调用。 #### 错误示例分析 ```java public class FileUploader { public static void uploadFile() { // 错误:直接静态调用实例方法 FileChannel.transferTo(...); // ❌ 编译错误 } } ``` #### 正确使用方式 ```java import java.io.*; import java.nio.channels.FileChannel; public class NfsUploader { public static void uploadWithTransferTo(File source, File target) throws IOException { try (FileInputStream fis = new FileInputStream(source); FileOutputStream fos = new FileOutputStream(target)) { // 1. 获取通道实例 FileChannel inChannel = fis.getChannel(); FileChannel outChannel = fos.getChannel(); // 2. 通过实例调用transferTo long position = 0; long size = inChannel.size(); while (position < size) { // 每次传输最大2GB(防止整数溢出) long chunkSize = Math.min(size - position, Integer.MAX_VALUE); position += inChannel.transferTo(position, chunkSize, outChannel); } } } public static void main(String[] args) throws IOException { File source = new File("large_file.dat"); File target = new File("/nfs/mount/large_file.dat"); uploadWithTransferTo(source, target); } } ``` #### 关键点解释: 1. **实例方法 vs 静态方法**: - `transferTo()` 是 `FileChannel` 类的实例方法 - 必须先创建 `FileChannel` 实例(通过 `FileInputStream.getChannel()` 或 `FileOutputStream.getChannel()`) 2. **分块传输的必要性**: - 单次传输上限为 2GB(`Integer.MAX_VALUE` 字节) - 循环确保完整传输超大文件 - 避免 `IOException: File too large` 3. **资源管理**: - 使用 try-with-resources 自动关闭流和通道 - 防止文件描述符泄漏 #### 替代方案:使用 NIO.2 Path API ```java import java.nio.file.*; public class PathUploader { public static void uploadWithNio(Path source, Path target) throws IOException { // 直接使用Path-to-Path复制(底层自动优化) Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES); } } ``` **优势**: - 无需手动管理通道 - JVM 自动选择最优传输方式 - 支持原子操作和属性复制 #### 常见错误场景排查: | 错误场景 | 解决方案 | |----------|----------| | 在静态方法中直接调用 `FileChannel.transferTo()` | 通过 `FileInputStream/FileOutputStream` 获取通道实例 | | 未处理分块传输 | 添加 `while(position < size)` 循环 | | 忽略传输返回值 | 使用 `position += channel.transferTo(...)` 更新位置 | | 未关闭通道 | 使用 try-with-resources 语句 | ### 性能最佳实践 ```java // 优化缓冲区大小(Linux 默认 128KB) System.setProperty("jdk.nio.channel.defaultMaxTransmitSize", "1048576"); // 1MB // 使用直接缓冲区(堆外内存) try (FileChannel in = FileChannel.open(source, StandardOpenOption.READ); FileChannel out = FileChannel.open(target, StandardOpenOption.WRITE, StandardOpenOption.CREATE)) { out.force(true); // 强制刷盘 in.transferTo(0, in.size(), out); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值