Java FileReader 内存使用模式

Java FileReader memory usage pattern

似乎 FileReader 正在将文件读入内存,但我不希望它这样做。根据 OS 报告的 java 的内存使用情况,我的简单程序从最初的 ~11MB 攀升到 60MB 以上,同时读取一个 ~50MB 的文件。只是作为基线,以下没有 FileReader 的程序运行了大约 30 秒,并保持固定在 ~11MB 内存使用。

public class TestClass {
    public static void main(String[] args) throws Exception {
        int count=0;
        while ((count++) < 30000) {
            Thread.sleep(1);
        }
    }
}

但是,一旦我添加了 FileReader,在 java 进程结束之前,内存使用将从 ~11MB 攀升至超过 60MB。但是根据文档,"FileReader is meant for reading streams of characters."我错过了什么?

import java.io.FileReader;

public class TestClass {
    public static void main(String[] args) throws Exception {
        FileReader reader = new FileReader("/tmp/big.txt");
        int charCode = -1;
        while ((charCode = reader.read()) > -1) {
            Thread.sleep(1);
        }
        reader.close();
    }
}

当您 运行 JVM 时,此 JVM 从底层系统分配 虚拟 内存。因此,只有当有东西被写入内存页面时,内存才真正被分配。这意味着当你实例化一个新对象时,内存是真正分配的。因为实例化一个新对象会将一些数据写入内存。但是当对象被丢弃时,JVM 不会将这件事告诉操作系统。所以从操作系统的角度来看,内存仍然是分配的。但是,当然,之后新对象 可以 由 JVM 重新分配到相同的内存位置,因为 JVM 知道哪个内存 space 是空闲的。但这绝对不是许多 JVM 强制执行的:它们不需要在先前丢弃的对象的相同内存位置分配新对象,因为它们有大量的 virtual 内存。

因此,当调用 reader.read() 一次时,系统库会为临时对象(读取数据)分配一些内存,并在返回之前取消引用这些对象。但是收集这些对象的垃圾收集器不会告诉 OS 有关相应块的信息。

这就像没有 TRIM SSD 磁盘:磁盘不知道丢弃的和可用的块。

无论如何,有人可以创建一个具有不同内存使用行为的 JVM,因为该行为留给了实现者。

我的猜测是,内存使用量的增加是由于加载了所有文件 I/O 支持软件,而且内存使用量和文件大小的增加量均为 ~50MB 只是巧合。您可以通过尝试使用明显不同的文件大小进行实验来检查这一点。如果您试图尽量减少内存使用,您也可以尝试在调用 Thread.sleep(1); 之前调用 System.gc();。底层 I/O 框架可能会生成垃圾(重新分配缓冲区等),即使您的代码几乎没有垃圾。