问题描述
public String tpeResponse(InputStream is) {
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder out = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
out.append(line);
}
reader.close();
is.close();
return out.toString();
}
java.lang.OutOfMemoryError:Java 堆空间
请帮助我使用 Streams 和 lambda 表达式重建这个方法。
解决方法
无论您从(传递给您的方法的那个 is
变量)读取数据是
-
[A] 无穷无尽(例如,如果它是
System.in
,它是一个InputStream
,代表您的流程标准,并且您使用java -jar yourapp.jar </dev/null
启动应用程序,那么它实际上是无止境。 -
或者,[B] 不是无穷无尽的,而是 LOT 的数据。例如,相同的情况,但您执行
java -jar yourapp.jar </my/20gigabyte4kCopyOfTheEntireLordOfTheRingsTrilogy.mp4
。
a StringBuilder
只是将所有数据存储在有限的进程内存中。限制到什么程度?好吧,当然不会超过您计算机中的 RAM 总量,但通常会少于此数量,这取决于您如何启动 JVM。但是,如果您达到了该限制,答案是无论如何都不要使用 StringBuilder。
有几个解决方案。哪一个是正确的?从您在问题中提供的有限细节无法判断:
- 意识到它是无限输入,因此根本不可能尝试存储所有输入。
- 采用流式模型,而不是将所有输入转换为一个大字符串然后处理该字符串,而是获取足够的输入,您可以对其进行操作,对其进行操作,然后继续进行。
例如,假设您有一个应用程序,它计算输入流中“嘿”一词出现的次数。
与其像你那样写,不如你可以这样做:
public int countHey(InputStream is) {
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line;
int total = 0;
while ((line = reader.readLine()) != null) {
total += countHeyInLine(line);
}
reader.close();
is.close(); // WARNING! SEE BELOW!
return total;
}
即使你把收集的莎士比亚作品提供给它,这段代码也能工作,因为这段代码读取一行,处理那一行,把这个处理的结果减少到一个单一的值,然后继续:Java 的垃圾收集器可以简单地收集您处理过的所有行,因此此代码将愉快地挖掘您扔给它的所有数百万行,而不会耗尽内存。
注意:无论打开资源还是关闭它,或者需要非常清楚地标记,它将关闭资源的责任转移给调用它的人。您做了相反的操作:此方法不是创建该输入流的代码,但您正在关闭它。这是一个非常糟糕的主意,会导致资源泄漏。您不应该关闭 is
并在文档中明确表示您的代码没有。或者,如果您认为此方法最好关闭 is
,它应该在 try/finally 结构中这样做,以便调用者可以放心,调用此方法将最终以某种方式关闭该流。