内存不足错误:将数字基元转换为字符时的 Java 堆空间



我一直在研究装饰器模式并开发了简单的类ToUpperCaseInputStream。我覆盖了 read() 方法,因此它可以将所有字符从输入流转换为大写。该方法的代码如下所示(抛出 OutOfMemoryError):

@Override
public int read() throws IOException {
    return Character.toUpperCase((char)super.read());
}

正如我后来发现的那样,投射到字符是多余的,但这不是重点。我有"java.lang.OutOfMemoryError: Java heap space",当代码:

((char) super.read())  

评估。为了使它更简单,我编写了相同的方法(这个抛出 OutOfMemoryError):

@Override
public int read() throws IOException {
    int c =(char) super.read();
    return (c == -1 ? c : Character.toUpperCase(c));
} 

而这个没有:

@Override
public int read() throws IOException {
    int c = super.read();
    return (c == -1 ? c : Character.toUpperCase(c));
} 

当我从赋值中删除强制转换时,代码运行没有错误,并导致所有文本大写。正如甲骨文教程中所说:

对引用类型 (§15.26.1)、方法调用表达式 (§15.12) 或前缀或后缀增量 (§15.14.2, §15.15.1) 或递减运算符 (§15.14.3

, §15.15.2) 的数组组件的赋值都可能由于装箱转换 (§5.1.7) 而引发 OutOfMemoryError

似乎使用了自动装箱,但对我来说并非如此。同一方法的两个变体都会导致内存不足错误。如果我错了,请向我解释一下,因为它会炸毁我的头。

要提供更多信息,请提供客户端代码:

public class App {
public static void main(String[] args) throws IOException {
    try (InputStream inet = new ToUpperCaseInputStream(new FileInputStream("d:/TEMP/src.txt"));
        FileOutputStream buff = new FileOutputStream("d:/TEMP/dst.txt")) {
        copy(inet, buff);
    }
}
public static void copy(InputStream src, OutputStream dst) throws IOException {
    int elem;
    while ((elem = src.read()) != -1) {
        dst.write(elem);
    }
}

}

它的作用只是将简单的消息从一个文件打印到另一个文件。

虽然这个案子已经解决了,但我想分享一个关于如何进行铸造的非常好的解释。 https://stackoverflow.com/a/24637624/1923644

在转换为 char 之前,您需要检查 -1(输入数据的信号结束)。

Java 中的 char 是一个无符号的短字符,这意味着当返回 -1 时,您的强制转换将使其成为 65535。即使您没有 OutOfMemory,您的代码仍然会损坏。

关于为什么会出现 OOM 错误,如果没有完整的代码,很难说,也许在代码的后面会有一些基于字符值的内存分配。

所以试试这个,看看它是否有帮助:

@Override
public int read() throws IOException {
    int c = super.read();
    if (c == -1) return c;
    char ch = (char) c;
    return Character.toUpperCase(ch);
} 

最新更新