【问题标题】:Why does java Grep crash with OutOfMemoryError?为什么 java Grep 会因 OutOfMemoryError 而崩溃?
【发布时间】:2011-06-22 03:05:43
【问题描述】:

我或多或少地运行以下代码

http://download.oracle.com/javase/1.4.2/docs/guide/nio/example/Grep.java

我正在使用以下 VM 参数

-Xms756m -Xmx1024m

它在 400mb 文件上因 OutOfMemory 崩溃。我做错了什么?

堆栈跟踪:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.nio.HeapCharBuffer.<init>(Unknown Source)
    at java.nio.CharBuffer.allocate(Unknown Source)
    at java.nio.charset.CharsetDecoder.decode(Unknown Source)
    at com.alluvialtrading.tools.Importer.<init>(Importer.java:46)
    at com.alluvialtrading.tools.ReutersImporter.<init>(ReutersImporter.java:24)
    at com.alluvialtrading.tools.ReutersImporter.main(ReutersImporter.java:20)

【问题讨论】:

    标签: java out-of-memory


    【解决方案1】:

    你没有做错什么。

    问题是应用程序将整个文件映射到内存中,然后创建文件的第二个堆内副本。映射文件不占用堆空间,尽管它确实使用了 JVM 的部分虚拟地址空间。

    这是第二个副本,创建它的过程实际上是填充堆。第二个副本包含扩展为 16 位字符的文件内容。考虑到堆空间的分区方式,约 4 亿字符(8 亿字节)的连续数组对于 1Gb 堆来说太大了。

    简而言之,应用程序只是使用了过多的内存。

    您可以尝试增加最大堆大小,但真正的问题是应用程序在管理内存的方式上过于简单。


    要说明的另一点是,您正在运行的应用程序是一个旨在说明如何使用 NIO 的示例。它不是为通用的、生产质量的实用程序而设计的。您需要相应地调整您的期望。

    【讨论】:

    • 我虽然内存映射不消耗实际内存,但它只是将应用程序的一些虚拟页面映射到指向硬盘驱动器。我错了吗?
    • @tulskiy - 它还使用了一大块进程虚拟地址空间,这受硬件和操作系统的限制。例如。在默认的 32 位 Windows 安装中,应用程序的虚拟地址空间限制为 2Gb。
    • @Stephen C:但在这种情况下,它会抛出“无法分配内存”之类的东西。所以内存映射实际上并不会消耗太多堆大小。
    • @tulskiy - OP 只告诉我们这是一个 OOME,而不是实际的异常消息是什么。内存映射文件失败也可能引发 OOME。
    • @Stephen C:此代码在decoder.decode(bb) 中引发 OOME。我在一个 400M 的文件上给了它 2Gb 的堆(虽然它是一个二进制文件),但它仍然不起作用。
    【解决方案2】:

    可能是因为 400Mb 的文件被加载到 CharBuffer 中,所以它在 UTF16 编码中占用了两倍的内存。所以它不会为模式匹配器留下太多内存。

    如果您使用的是最新版本的 java,请尝试使用 -XX:+UseCompressedStrings,以便它在内部将字符串表示为字节数组并消耗更少的内存。您可能必须将 CharBuffer 放入字符串中。

    所以例外是

    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at java.nio.HeapCharBuffer.<init>(HeapCharBuffer.java:57)
        at java.nio.CharBuffer.allocate(CharBuffer.java:329)
        at java.nio.charset.CharsetDecoder.decode(CharsetDecoder.java:777)
        at Grep.grep(Grep.java:118)
        at Grep.main(Grep.java:136)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    

    下面的行是 HeapCharBuffer 的构造函数:

    super(-1, 0, lim, cap, new char[cap], 0);
    

    这意味着它无法创建文件大小的char 数组。

    如果您想在 java 中 grep 大文件,您需要找到一些接受某种Reader 的算法。标准 java 库没有这样的功能。

    【讨论】:

    • "-XX:+UseCompressedStrings" 不太可能有帮助。关键问题是CharBuffer 而不是String
    【解决方案3】:

    我假设是因为给定的类将整个文件加载到内存中。确切地我不确定,因为我不知道 Java NIO 类。我怀疑像MappedByteBufferCharBuffer 这样的类可能是问题所在。

    堆栈跟踪可能会告诉您它的来源。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-08-23
      • 1970-01-01
      • 1970-01-01
      • 2017-06-06
      • 2011-02-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多