Scala中OutOfMemoryError的原因是什么?

TBZ*_*Z92 1 java scala heap-memory out-of-memory

我刚刚开始学习来自Python的Scala.我在Scala中尝试了一个基本的文件处理任务.任务是"[ ... ]"使用正则表达式从数据文件中删除子串.该脚本成功处理前几个文件,然后抛出java.lang.OutOfMemoryError: Java heap space错误.发生错误的数据文件大约是70MB,我有16GB的RAM供我使用.(前面的6个文件的文件大小<100Kb,第一个文件作为例外:5.5MB).

我的问题是:是什么导致了 OutOfMemoryError,我怎样才能改变我的方法以防止它发生?我不明白为什么会这样.我在调试内存错误方面经验不足,因为Python在内存管理方面相对宽容.

关于编码风格或我使用的方法的任何其他评论都非常受欢迎 - 我渴望学习.

Regexer.scala:

import scala.io.Source 
import java.io._

object Regexer {

  def main(args: Array[String]): Unit = {

    val filenames = Source.fromFile("all_files.txt").getLines()

    for (fn <- filenames) {

        val datafile:String = Source.fromFile(fn).mkString

        val new_data:String = datafile.replaceAll(raw"\[.*?\]", "")

        val file = new File(fn)         
        val bw = new BufferedWriter(new FileWriter(file))
        bw.write(new_data)
        bw.close()


    }   
  } 
}
Run Code Online (Sandbox Code Playgroud)

all_files.txt 是一个文件,包含要处理的所有文件的路径(因为它们位于子目录中).

最后,执行时抛出完整的错误消息:

java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:3332)
    at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
    at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:596)
    at java.lang.StringBuilder.append(StringBuilder.java:190)
    at scala.collection.mutable.StringBuilder.appendAll(StringBuilder.scala:249)
    at scala.io.BufferedSource.mkString(BufferedSource.scala:97)
    at Regexer$$anonfun$main$1.apply(Regexer.scala:12)
    at Regexer$$anonfun$main$1.apply(Regexer.scala:10)
    at scala.collection.Iterator$class.foreach(Iterator.scala:893)
    at scala.collection.AbstractIterator.foreach(Iterator.scala:1336)
    at Regexer$.main(Regexer.scala:10)
    at Regexer.main(Regexer.scala)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at scala.reflect.internal.util.ScalaClassLoader$$anonfun$run$1.apply(ScalaClassLoader.scala:70)
    at scala.reflect.internal.util.ScalaClassLoader$class.asContext(ScalaClassLoader.scala:31)
    at scala.reflect.internal.util.ScalaClassLoader$URLClassLoader.asContext(ScalaClassLoader.scala:101)
    at scala.reflect.internal.util.ScalaClassLoader$class.run(ScalaClassLoader.scala:70)
    at scala.reflect.internal.util.ScalaClassLoader$URLClassLoader.run(ScalaClassLoader.scala:101)
    at scala.tools.nsc.CommonRunner$class.run(ObjectRunner.scala:22)
    at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:39)
    at scala.tools.nsc.CommonRunner$class.runAndCatch(ObjectRunner.scala:29)
    at scala.tools.nsc.ObjectRunner$.runAndCatch(ObjectRunner.scala:39)
    at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:65)
    at scala.tools.nsc.MainGenericRunner.run$1(MainGenericRunner.scala:87)
    at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:98)
    at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:103)
    at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)
Run Code Online (Sandbox Code Playgroud)

puh*_*len 8

您的计算机上可能有16Gib,但这并不意味着JVM可以使用所有这些.Scala代码(通常)在Java虚拟机(JVM)中运行,它具有自己的内存.您可用的默认内存量可能对您的程序而言太低.可以使用该-Xmx选项设置进程的最大可用内存.尝试类似的东西,java -Xmx1024m Regexer或者java -Xmx2g Regexer你认为应该有多少记忆.如果在添加处理文件所需的可用内存后仍然遇到问题,那么您可能会发生内存泄漏,或者您的算法需要进行优化.

在您的特定情况下,不要将整个文件加载到内存中,而是考虑逐行处理或一些其他缓冲区数量,以便您在任何时候只需要将一小部分文件保留在内存中

  • 正如我所说,这样做是将参数传递给您的程序而不是JVM,所以实际上您仍然使用默认的最大堆大小.如果您使用scala命令而不是java命令来运行代码,则需要编写`scala -J-Xmx2g MyClass`或将`-Xmx2g`选项添加到`JAVA_OPTS`环境变量中. (2认同)