簡體   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