获得Out of Memory Exception的根本原因是什么?我们怎样才能克服这一点?

时间:2016-05-11 12:51:14

标签: java garbage-collection inputstream heap-memory outputstream

这是我的输出流读取和写入的示例代码段,我的内存不足。

 public static void readFileContent(InputStream in, OutputStream out) throws IOException {
    byte[] buf = new byte[500000];
    int nread;
    int navailable;
    int total = 0;
    synchronized (in) {
        try {
            while((nread = in.read(buf, 0, buf.length)) >= 0) {
                out.write(buf, 0, nread);
                total += nread;
            }
        }
        finally {
            if (in != null) {
                try {
                    in.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    out.flush();
    buf = null;
}
  
      
  1. 使用上述代码段可以获得"内存异常" ?
  2.   
  3. 是否有必要在此处关闭输出流?并且流,冲洗是否足够或者我们是否需要始终关闭流?如果是这样的原因?
  4.   
  5. 我怎么能避免一般的内存不足异常?
  6.   

请澄清我。

4 个答案:

答案 0 :(得分:1)

  
      
  1. 使用上述代码段可以获得"内存异常" ?
  2.   

内存不足异常有多种根本原因。有关更多详细信息,请参阅oracle文档page

<强> java.lang.OutOfMemoryError: Java heap space

原因:详细消息Java堆空间表示无法在Java堆中分配对象。

<强> java.lang.OutOfMemoryError: GC Overhead limit exceeded

原因:详细消息&#34; GC开销限制超出&#34;表示垃圾收集器一直在运行,Java程序进展非常缓慢

<强> java.lang.OutOfMemoryError: Requested array size exceeds VM limit

原因:详细消息&#34;请求的数组大小超过VM限制&#34;表示应用程序(或该应用程序使用的API)尝试分配大于堆大小的数组。

<强> java.lang.OutOfMemoryError: Metaspace

原因: Java类元数据(Java类的虚拟机内部表示)在本机内存中分配(此处称为元空间)

<强> java.lang.OutOfMemoryError: request size bytes for reason. Out of swap space?

原因:详细信息&#34;请求大小字节的原因。没有交换空间?&#34;似乎是OutOfMemoryError异常。但是,当来自本机堆的分配失败并且本机堆可能接近耗尽时,Java HotSpot VM代码会报告此明显异常

  
      
  1. 是否有必要在此处关闭输出流?并且流,冲洗是否足够或者我们是否需要始终关闭流?如果是这样的原因?
  2.   

因为您在方法中使用了原始InputStreamOutputStream,所以我们不会这样做。知道哪种类型的实际Stream传递给此方法,因此明确关闭这些Streams是个好主意。

  
      
  1. 我怎么能避免一般的内存不足异常?
  2.   

这个问题已经回答了您的第一个问题。

请参阅有关处理IO操作的大文件的SE问题:

Java OutOfMemoryError in reading a large text file

答案 1 :(得分:0)

我认为很明显,问题是您一次分配500000个字节,并且它们在运行时可能无法在堆中使用。

<强>说明: 我不建议,但你可以增加程序的堆大小。 determined at runtime,但也可以参数化。

<强>建议: 据我所知,通过提供的代码片段,一次读取500000字节并非绝对必要。因此,您可以使用较小的数字初始化字节数组,从而产生更多的读取循环。但如果它对你的程序来说不是问题......我想。

<强>结论: 尝试将初始字节数组大小设置为5000,甚至1000

编辑:

需要考虑的另一点是,在上面的代码片段中,您最后只会刷新一次。您写入OutputStream的字节将保留在内存中,其大小也可能会导致OutOfMemoryException

为了克服这个问题,你应该更频繁地冲洗。如果你经常冲洗,它会影响你的表现,但你总是可以在循环中试验一个条件,例如

...
if (total % 5000 == 0) {
    out.flush();
}
...

编辑2:

由于InputStreamOutputStream对象作为参数传递给给定方法,因此,在我看来,此方法不负责关闭它们。初始化Streams的方法也负责优雅地关闭它们。 Flush就足够了。但是考虑用更小的块来做。

编辑3:

总结建议的调整:

public static void readFileContent(InputStream in, OutputStream out) throws IOException {
    byte[] buf = new byte[1000];
    // wrap your OutputStream in a BufferedOutputStream
    BufferedOutputStream bos = new BufferedOutputStream(out, 5000);
    int nread;
    int navailable;
    int total = 0;
    synchronized (in) {
        try {
            while((nread = in.read(buf, 0, buf.length)) >= 0) {
                // use the BufferedOutputStream to write data
                // you don't need to flush regularly as it is handled automatically every time the buffer is full
                bos.write(buf, 0, nread);
                total += nread;
            }
        }
        finally {
            if (in != null) {
                try {
                    in.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    // flush the last contents of the BufferedOutputStream
    bos.flush();
    buf = null;
}

另请注意,BufferedOutputStream会在您正常关闭时自动致电flush()

编辑4:

调用上述方法的示例:

public static void main(String[] args) {
    String filename = "test.txt";
    String newFilename = "newtest.txt";

    File file = new File(filename);
    File newFile = new File(newFilename);

    try (InputStream fis = new FileInputStream(file);
            OutputStream fout = new FileOutputStream(newFile)) {
        readFileContent(fis, fout);
    }
    catch(IOException ioe) {
        System.out.println(ioe.getMessage());
    }
}

答案 2 :(得分:0)

  1. 将buf更改为新字节[1 * 1024]
  2. 只使用buf阅读,无需指定长度,例如 pos = in.read(buf)
  3. 其余代码看起来不错。无需增加内存。 此外,同步inputStream的任何点?

答案 3 :(得分:-1)

在Java中,没有任何可靠的释放内存的方法。即使调用内置垃圾收集器(System.gC())也可能无法解决问题,因为GC只释放不再引用的对象。您需要处理您正在编写的代码,以便它可以以最佳方式使用资源。当然,有些情况下你被排除在选项之外,特别是当你使用大型或巨型数据结构而不管你能想到的任何代码优化时(在你的情况下,你创建的是一个包含50万字节记录的数组)

作为部分解决方案,您可以增加堆大小内存,以便Java可以分配更多内存。