简体   繁体   English

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

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

Here is my sample snippet for reading and writing by output stream and I am getting out of memory exception. 这是我的输出流读取和写入的示例片段,我的内存异常。

 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. What are the possible scenarios with the above snippet to get "out of memory exception" ? 使用上述代码段获取“内存不足异常”的可能情况有哪些?
  2. Is it necessary to close the output stream here? 是否有必要在此处关闭输出流? And does stream, flush is enough or do we need to close the stream always? 并且流,冲洗是否足够或者我们是否需要始终关闭流? If so why? 如果是这样的话?
  3. How could I avoid Out of memory exception in general? 我怎么能避免一般的内存不足?

Please clarify me. 请澄清我。

  1. What are the possible scenarios with the above snippet to get "out of memory exception" ? 使用上述代码段获取“内存不足异常”的可能情况有哪些?

There are various root causes for out of memory exceptions. 内存不足异常有各种根本原因。 Refer to oracle documentation page for more details. 有关更多详细信息,请参阅oracle文档页面

java.lang.OutOfMemoryError: Java heap space : java.lang.OutOfMemoryError: Java heap space

Cause : The detail message Java heap space indicates object could not be allocated in the Java heap. 原因 :详细消息Java堆空间表示无法在Java堆中分配对象。

java.lang.OutOfMemoryError: GC Overhead limit exceeded : java.lang.OutOfMemoryError: GC Overhead limit exceeded

Cause: The detail message "GC overhead limit exceeded" indicates that the garbage collector is running all the time and Java program is making very slow progress 原因:详细消息“超出GC开销限制”表示垃圾收集器一直在运行,Java程序进展非常缓慢

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

Cause : The detail message "Requested array size exceeds VM limit" indicates that the application (or APIs used by that application) attempted to allocate an array that is larger than the heap size. 原因 :详细消息“请求的数组大小超过VM限制”表示应用程序(或该应用程序使用的API)尝试分配大于堆大小的数组。

java.lang.OutOfMemoryError: Metaspace : java.lang.OutOfMemoryError: Metaspace

Cause: Java class metadata (the virtual machines internal presentation of Java class) is allocated in native memory (referred to here as metaspace) 原因: Java类元数据(Java类的虚拟机内部表示)在本机内存中分配(此处称为元空间)

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

Cause: The detail message "request size bytes for reason. Out of swap space?" 原因:详细消息“请求大小字节的原因。超出交换空间?” appears to be an OutOfMemoryError exception. 似乎是OutOfMemoryError异常。 However, the Java HotSpot VM code reports this apparent exception when an allocation from the native heap failed and the native heap might be close to exhaustion 但是,当来自本机堆的分配失败并且本机堆可能接近耗尽时,Java HotSpot VM代码会报告此明显异常

  1. Is it necessary to close the output stream here? 是否有必要在此处关闭输出流? And does stream, flush is enough or do we need to close the stream always? 并且流,冲洗是否足够或者我们是否需要始终关闭流? If so why? 如果是这样的话?

since you are using raw InputStream and OutputStream in your method, we don' t know which type of actual Stream is getting passed to this method and hence explicitly close these Streams is good idea. 因为您在方法中使用原始InputStreamOutputStream ,所以我们不知道哪种类型的实际Stream传递给此方法,因此明确关闭这些Streams是个好主意。

  1. How could I avoid Out of memory exception in general? 我怎么能避免一般的内存不足?

This question is already answered in response to your first question. 这个问题已经回答了您的第一个问题。

Refer to this SE question on handling large files for IO operations : 有关处理IO操作的大文件,请参阅此SE问题:

Java OutOfMemoryError in reading a large text file Java OutOfMemoryError在读取大文本文件时

I think it's obvious that the problem is that you allocate 500000 bytes at once, and they may not be available in the heap at runtime. 我认为很明显问题是你一次分配500000个字节,并且在运行时它们可能在堆中不可用。

Explanation: I would not suggest it, but you could increment the heap size of your program. 说明:我不建议,但您可以增加程序的堆大小。 The default heap size for a java program is determined at runtime , but it can also be parameterized. java程序的默认堆大小是在运行时确定的 ,但也可以参数化。

Recommendation: As far as I can see by the provided snippet, it's not absolutely necessary to read 500000 bytes at once. 建议:据我所知,通过提供的代码片段,一次读取500000字节并非绝对必要。 So, you can initialize your byte array with a smaller number that would result in having more reading loops. 因此,您可以使用较小的数字初始化字节数组,从而产生更多的读取循环。 But if it's not a problem for your program... I guess. 但如果这不是你的程序的问题......我想。

Conclusion: Try by setting the initial byte array size to 5000 , or even 1000 . 结论:尝试将初始字节数组大小设置为5000 ,甚至1000

EDIT: 编辑:

An extra point to take into consideration is that in the above code snippet you only flush once at the end. 需要考虑的另一点是,在上面的代码片段中,您只需在结尾处刷新一次。 The bytes you are writting to the OutputStream are kept in memory, and their size may cause an OutOfMemoryException also. 您写入OutputStream的字节保存在内存中,它们的大小也可能导致OutOfMemoryException

In order to overcome this, you should flush more often. 为了克服这个问题,你应该更频繁地冲洗。 It will affect your performance if you flush too often, but you can always experiment with a condition in your loop eg 如果你经常冲洗它会影响你的表现,但是你总是可以在你的循环中试验一个条件,例如

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

EDIT 2: 编辑2:

As the InputStream and OutputStream objects are passed to the given method as parameters, so, in my opinion this method is not responsible for closing them. 由于InputStreamOutputStream对象作为参数传递给给定方法,因此,在我看来,此方法不负责关闭它们。 The method that initializes the Streams is also responsible for close them gracefully. 初始化Streams的方法也负责优雅地关闭它们。 Flush is enough for this method. 对于这种方法, Flush就足够了。 But consider doing it in smaller chunks. 但考虑在较小的块中进行。

EDIT 3: 编辑3:

To summarize the suggested tweaks: 总结建议的调整:

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;
}

Please note also that BufferedOutputStream will automatically call flush() when you will close it gracefully. 请注意,当您正常关闭它时, BufferedOutputStream将自动调用flush()

EDIT 4: 编辑4:

Example calling the above method: 调用上述方法的示例:

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. Change the buf to new byte[1*1024] 将buf更改为新字节[1 * 1024]
  2. Read using just buf no need to specify length eg pos = in.read(buf) 只使用buf读取无需指定长度,例如pos = in.read(buf)

The rest of the code looks good. 其余代码看起来不错。 No need to increase the memory. 无需增加内存。 Also, any points of synchronised inputStream? 此外,同步inputStream的任何点?

In Java there isn't any brute way of freeing memory. 在Java中,没有任何蛮横的释放内存的方法。 Even calling the built-in Garbage Collector ( System.gC() ) might not solve the problem, as the GC only frees objects that are not referenced anymore. 即使调用内置垃圾收集器( System.gC() )也可能无法解决问题,因为GC只释放不再引用的对象。 You need to take care of the code that you are writing so that it can employ the resources in its best way it can. 您需要处理您正在编写的代码,以便它可以以最佳方式使用资源。 Certainly, there are cases that you are left out of options, especially when you are using big or giant data structures regardless of any code optimization you can think of (in your case, you are creating an array with half a million records of bytes). 当然,有些情况下你被排除在选项之外,特别是当你使用大型或巨型数据结构而不管你能想到的任何代码优化时(在你的情况下,你创建的是一个包含50万字节记录的数组) 。

As a partial solution, you can increase your Heap Size Memory so that Java can allocate more memory. 作为部分解决方案,您可以增加堆大小内存,以便Java可以分配更多内存。

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

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