使用InputStream会导致内存泄漏吗?

4
我正在使用Java开发一个文件读取和保存的应用程序,因此我使用流(streams)来实现。首先从文件中获取一个stream,然后使用这个stream在另一个文件夹中创建一个新文件。问题是,在使用完stream并关闭它之后,应该释放的内存仍然存在。
例如,我加载了一个100MB的文件(任务管理器显示java.exe增加了100MB),然后将文件保存到另一个文件夹,并使用stream.close()关闭流。但是,java.exe没有减少100MB。当我多次保存和加载文件时,java.exe超过600MB,然后降至300MB左右。每次接近600MB时就会降到300MB。为什么会出现这种情况?为什么在调用stream.close()时不能将内存归零?为什么只有在接近600MB时才释放内存,而且不会释放所有内存?
以下是我如何从字符串路径加载流并关闭流的示例:
String path = ... //File path
InputStream stream = new BufferedInputStream(new FileInputStream(path));
stream.close();

感谢您的回复。

3
如果你决定使用那个内存,有可能会被释放以供你使用,这意味着“过度”使用是无关紧要的。垃圾回收并不总是立即发生;请参见http://javarevisited.blogspot.com/2011/04/garbage-collection-in-java.html。 - Robert Harvey
释放内存给操作系统是一项昂贵的操作。 - Thorbjørn Ravn Andersen
我一直以为Java只会很少地将内存释放给操作系统——在进行小型收集过程时绝对不会。这是基于这样一个假设,即大多数需要X数量内存的软件在某个时间点需要再次使用它。 - millimoose
3个回答

4

BufferedInputStream默认使用8KB的固定大小,因此最多只会泄漏8KB(加上一些开销)

我加载了一个100MB的文件(任务管理器显示java.exe增加了100MB),

这意味着您使用了100MB的对象来处理该文件。

但是java.exe不会减少100MB

没有理由它应该减少。它很可能在GC之后会减少,但您不想这样做,除非必要,我猜您不需要。

每当它即将超过600MB时,它就会减少到300MB。为什么?

您触发了垃圾回收。很可能是一个次要的集合。

为什么当我调用stream.close()时,内存不能归零?

它在开始时不是0 MB,当您进行GC时,将会收回内存,但您不希望毫无必要地这样做。

为什么当它约为600MB时释放内存,而且不释放所有内存?

它有多个内存区域,并尝试执行最简单的操作,通常是清理Eden空间。


很可能是一个小型收集 - 一个小型收集只会释放伊甸园空间。我认为在GC期间,只有将幸存者从伊甸园移动到较旧的一代,才能使所有伊甸园再次可用。这意味着一个小型收集实际上会增加内存使用量,而不是减少它。 (也就是说,这是基于旧版HotSpot分代清道夫白皮书/指南的模糊回忆,现在可能有所不同。) - millimoose
小型集合会释放包括幸存者空间在内的年轻代。一旦伊甸园空间被清除,您将永远不会比开始时拥有更多的数据。 - Peter Lawrey
是的,但是这个集合也会释放内存给操作系统吗? - millimoose
一般来说不会。JVM在启动时会保留虚拟内存。它可以将内存传递回操作系统,但对于正在运行的程序而言,这种情况很少发生。 - Peter Lawrey

2
你所看到的是Java内存系统的正常运行,分配的内存不一定会归还给操作系统。
不要通过操作系统级别检查java.exe的内存,而应该使用内存分析工具,例如内置的JConsole工具(或免费的Eclipse MAT进行更深入的分析)。

-1

在你的代码中,FileInputStream是匿名实例。

由于文件较大,它可能存储在虚拟内存/虚拟RAM中。并且它不会关闭,直到Java启动垃圾回收。

创建一个新的命名实例并关闭FileInputStream。

这样应该可以解决问题。


1
-1:不存在“匿名实例”和“命名实例”这样的东西 - 将FileInputStream分配给本地变量不会导致任何行为差异。 BufferedInputStream在进行一些清理后关闭底层缓冲流。 - millimoose
我尝试将其用作局部变量和非局部变量,结果是相同的。无论如何还是谢谢。 - Antonio López

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接