繁体   English   中英

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

[英]What is the root cause of getting Out Of Memory Exception? How can we overcome this?

这是我的输出流读取和写入的示例片段,我的内存异常。

 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. 我怎么能避免一般的内存不足?

请澄清我。

  1. 使用上述代码段获取“内存不足异常”的可能情况有哪些?

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

java.lang.OutOfMemoryError: Java heap space

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

java.lang.OutOfMemoryError: GC Overhead limit exceeded

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

java.lang.OutOfMemoryError: Requested array size exceeds VM limit

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

java.lang.OutOfMemoryError: Metaspace

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

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

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

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

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

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

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

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

Java OutOfMemoryError在读取大文本文件时

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

说明:我不建议,但您可以增加程序的堆大小。 java程序的默认堆大小是在运行时确定的 ,但也可以参数化。

建议:据我所知,通过提供的代码片段,一次读取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());
    }
}
  1. 将buf更改为新字节[1 * 1024]
  2. 只使用buf读取无需指定长度,例如pos = in.read(buf)

其余代码看起来不错。 无需增加内存。 此外,同步inputStream的任何点?

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM