簡體   English   中英

在Java中讀取大數據文件時的巨大內存開銷

[英]Huge memory overhead when reading a large data file in java

我正在使用MNIST數據集進行測試,以進行深度學習神經網絡開發。 訓練集由60,000個序列組成,每個序列具有784個雙精度輸入值。 從文件中讀取數據到Java數組中的過程以某種方式導致大約4GB的內存開銷,該開銷在程序運行期間一直分配。 此開銷是為雙精度數組本身分配的60000 * 784 * 8 = 376MB的補充。 似乎發生了這種開銷,因為java除了在數值數組之外還存儲了文件的完整副本,但這也許是Scanner開銷。

消息人士稱,將文件作為流讀取會避免將整個文件存儲在內存中。 但是,流讀取仍然存在此問題。 我正在將Java 8與Intellij 2016.2.4一起使用。 這是流讀取代碼:

FileInputStream inputStream = null;
Scanner fileScan = null;
String line;
String[] numbersAsStrings;

totalTrainingSequenceArray = new double[60000][784];

try {
    inputStream = new FileInputStream(m_sequenceFile);
    fileScan = new Scanner(inputStream, "UTF-8");
    int sequenceNum = 0;
    line = fileScan.nextLine();//Read and discard the first line.
    while (fileScan.hasNextLine()) {
        line = fileScan.nextLine();
        numbersAsStrings = line.split("\\s+"); //Split the line into an array of strings using any whitespace delimiter.
        for (int inputPosition = 0; inputPosition < m_numInputs; inputPosition++) {
            totalTrainingSequenceArray[sequenceNum][inputPosition] = Double.parseDouble(numbersAsStrings[inputPosition]);
        }
        sequenceNum++;
    }
    if (fileScan.ioException() != null) {//Handle fileScan exception
        throw fileScan.ioException();
    }
} catch (IOException e) {//Handle the inputstream exception
    e.printStackTrace();
} finally {
    if (inputStream != null)  {
        try {
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    if (fileScan != null) {
        fileScan.close();
    }
}

在讀取並調用System.gc()之后,我嘗試將流和掃描儀設置為null,但這無濟於事。 這是掃描儀開銷問題嗎? 在不產生大量永久性開銷的情況下,讀取此大數據文件的最簡單方法是什么? 感謝您的投入。

您的代碼工作正常。 完整的GC后將實際使用380MB的堆。

Java渴望分配內存以最大程度地減少GC開銷,您可以使用-Xmx512m參數或使用其他GC(例如-XX:+UseConcMarkSweepGC-XX:MaxHeapFreeRatio=40來限制分配的內存大小。

定義“開銷”。 VM使用分配的堆在垃圾回收時間和執行速度之間進行平衡(您可以使用一些螺釘來影響其決策)。

規范是VM讓堆填滿,直到達到gc閾值,然后收集可以收集的所有垃圾,然后進行contine執行(這大大簡化了)。 這導致堆使用情況出現“鋸齒狀”模式(逐漸填充,然后堆使用率突然下降)。 對於以一定速度產生垃圾的代碼,這是完全正常的。

您可以影響的點是“牙齒”的高度(通過調整允許的堆和/或gc何時插入)。 垃圾創建的速度(堆使用的急劇增加)取決於所執行的代碼,它的范圍可以從零到最大可達到的分配速率。

您的閱讀代碼屬於創建大量小垃圾對象的類型:來自掃描儀的行,將行拆分成的部分。 如果您的堆足夠大,則可以在不收集任何垃圾的情況下讀取整個文件(最可能的情況是4GB堆設置)。

如果使堆變小,VM將更快地收集垃圾,從而減少內存使用量(同樣,您可以使用gc參數來強制以較小的堆百分比進行收集)。

盡管期望代碼僅使用為數組計算的內存量來運行是不合理的。 您在任務管理器中看到的只是VM使用的所有內存的累積。 其中包括堆棧,JRE所需的任何資源,本機庫和堆。

堆外的內存可能千差萬別,具體取決於程序使用多少線程,文件和其他資源。 作為一個非常粗略的經驗法則,JRE本身至少要使用20-50 MB,即使只是運行諸如“ Hello world”之類的簡單操作。

VM調整的問題,無論您只是調整堆大小還是微調gc參數,都是在問題集更改時必須重做(例如,對於當前文件,您可能不使用-Xmx512m,但是您需要調整下一個文件的值)。

或者,您可以嘗試減少創建的垃圾量,理想情況下為零。 您可以逐個字符地讀取字符並使用狀態機進行解析,而不是逐行讀取掃描器。 這將大大減少垃圾的產生,但會使代碼復雜得多

在許多情況下,最“有效”的解決方案就是完全不必擔心內存使用情況-通過着重於程序的進度,優化VM參數或代碼所花費的時間可能會更有效。 只要“開銷”不會妨礙您,為什么還要打擾?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM