2017-09-29 61 views
0

我正在使用BufferedWriter将文本写入Java中的文件。但是,我在构造函数中提供了自定义缓冲区大小。问题在于,它以大小写的方式向文件写入文件(例如,如果我将缓冲区大小设为8KB,则文件将为8KB写入一次)。但是,当我查看缓冲写入对象占用的内存(使用YourKit分析器)时,它实际上是给定缓冲区大小的两倍(本例中为16KB)。BufferedWriter缓冲区大小和占用内存

我试着看看内部实现,看看为什么会发生这种情况,我看到它正在创建一个给定大小的char数组。当它写入数组时,它占用两倍的缓冲区大小是有意义的,因为每个char占用2个字节。

我的问题是,在这种情况下,BufferedWriter如何管理只写8KB,它在缓冲区中存储16KB。这在技术上是正确的吗?因为每次只刷新8KB(一半),即使它有16KB的缓冲区。

回答

1

但我期望存储在字符数组中的所有字符在达到缓冲区大小(在我给出的例子中为16KB)时写入文件。

8K的字符占用16 KB的内存。正确。

现在让我们假设这些字符实际上都是在ASCII子集中。

当您使用Java将字符流写入输出文件时,根据某种编码方案将字符编码为字节流。 (例如,该编码由OutputStreamWriter类中的内容执行)

当您使用8位字符集/编码方案(如ASCII或Latin-1 ...)编码这些8K字符或编码为UTF- 8(!!)...每个字符被编码为1个字节。因此,冲洗包含那些的8K字符的缓冲区会生成8K字节的写入。

+0

谢谢@Stephen C.现在有了更好的见解 – Ravi

0

BufferedWriter的大小是char数组的大小。

public BufferedWriter(Writer out, int sz) { 
    super(out); 
    if (sz <= 0) 
     throw new IllegalArgumentException("Buffer size <= 0"); 
    this.out = out; 
    cb = new char[sz]; 
    nChars = sz; 
    nextChar = 0; 

    lineSeparator = java.security.AccessController.doPrivileged(
     new sun.security.action.GetPropertyAction("line.separator")); 
} 

单个字符不等于单个字节。它全部由你的字符编码来定义。

因此,要完全按照您所描述的执行任务,您必须切换到另一个类:BufferedOutputStream,其中内部缓冲区按字节数精确计算。

public BufferedOutputStream(OutputStream out, int size) { 
    super(out); 
    if (size <= 0) { 
     throw new IllegalArgumentException("Buffer size <= 0"); 
    } 
    buf = new byte[size]; 
} 
+0

谢谢Alex.I理解单个字符不是单个字节。但我期望存储在字符数组中的所有字符在达到缓冲区大小时写入文件(在我给出的示例中,这是16 KB,因为char数组占用16 KB)。但为什么没有发生?为什么只刷新8 KB的文件?请帮我理解 – Ravi

+1

这取决于你的文件编码。 Java使用_UTF-16_作为其内部char存储,这意味着每个字符使用2个字节进行存储。但是,如果您的字符只是ASCII,而您的文件编码是_UTF-8_,那么每个字符将只使用文件中的1个字节,因此是结果。 – Alex

+0

同意@Alex。请参阅https://stackoverflow.com/questions/7019504/in-what-encoding-is-a-java-char-stored-in。你有1)chars的内部内存表示2)bufferedWriter内部的char缓冲区,3)char到字节转换(它本身也可能使用byte []的内部缓冲区),并且每个char产生可变数量的字节。这弥补了复杂的内存使用量预测。 Bird-eye-view,是char buffer大小的2到4倍似乎是合理的。但最终,8个字符最终可能会将8个字节写入文件。或16.或者10 ... – GPI

0

它取决于用于写入文件的编码:ISO-8859-1将字符存储为单个字节,UTF-8将所有ASCII字符编码为单个字节。