首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

Java ByteArrayOutputStream OutOfMemoryError 兩種原因深度分析

     編輯:關於C++

錯誤現象

使用 ByteArrayOutputStream (BAOS) 累積大量資料時,可能在兩個不同位置拋出 OutOfMemoryError (OOM)。

原因一:擴容超過 Integer.MAX_VALUE (位置 1: write())

  • BAOS 內部用 byte[] buf,count 為 int。
  • 擴容邏輯:newSize = Math.max(buf.length << 1, count + len)。
  • 當 count + len > Integer.MAX_VALUE (2^31-1) 時,newSize 溢位變負,或 Arrays.copyOf 拋出 OutOfMemoryError: Requested array size exceeds VM limit。
  • 此為單一陣列大小上限限制 (約 2GB),非堆記憶體不足。

原因二:toByteArray() 複製陣列失敗 (位置 2)

  • toByteArray() 執行 Arrays.copyOf(buf, count),需再分配一個 count 大小的新陣列。
  • 若堆記憶體剩餘空間 < 所需大小,拋出 OutOfMemoryError: Java heap space。
  • 此時 buf 已持有資料,count 正確,可直接用 buf (需配合 count 判斷有效長度)。

驗證程式碼

public class BAOSOOMDemo {
    static class MyBAOS extends ByteArrayOutputStream {
        public byte[] getBuf() { return buf; }
        public int getCount() { return count; }
    }

    public static byte[] mapToBytes(Map map) {
        try (MyBAOS memOut = new MyBAOS();
             ObjectOutputStream out = new ObjectOutputStream(memOut)) {
            out.writeObject(map);
            out.flush();
            System.out.println("count=" + memOut.getCount() + ", buf.length=" + memOut.getBuf().length);
            // 避免 toByteArray() 第二次分配
            // return Arrays.copyOf(memOut.getBuf(), memOut.getCount()); // 手動複製
            return memOut.toByteArray(); // 可能 OOM
        } catch (IOException e) { throw new RuntimeException(e); }
    }

    public static void main(String[] args) {
        Map map = new HashMap<>();
        // 測試 300 萬條目,視記憶體調整
        for (int i=0; i<3000000; ++i) 
            map.put(String.valueOf(i), "abcdefghijklmnopqrstuvwxyz".repeat(10));
        byte[] data = mapToBytes(map);
        System.out.println("Final size: " + data.length);
    }
}

解決方案

  1. 分塊寫入:不要把超大物件一次序列化到 BAOS,改用 FileOutputStream 或分段序列化。
  2. 避免 toByteArray():若下游接受 InputStream,用 new ByteArrayInputStream(buf, 0, count) 包裝內部緩衝區 (需繼承暴露 buf/count)。
  3. 增加 Heap:-Xmx4g 等,治標不治本。
  4. 替代方案:ByteBuffer (支援直接記憶體)、MappedByteBuffer (記憶體映射檔案)、Netty CompositeByteBuf。

核心教訓

ByteArrayOutputStream 適用於中小型資料緩衝 (MB 級)。大資料量 (GB 級) 必須串流式處理,避免全量駐留堆記憶體。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved