Câu trả lời ngắn,
mà không làm bất cứ điều gì, bạn có thể đẩy giới hạn hiện tại bằng hệ số 1,5. Nó có nghĩa là, nếu bạn có thể xử lý 800MB, bạn có thể xử lý 1200 MB. Nó cũng có nghĩa là nếu có một số mẹo với java -Xm ....
bạn có thể di chuyển đến một điểm mà mã hiện tại của bạn có thể xử lý 7 GB, vấn đề của bạn được giải quyết, vì hệ số 1.5 sẽ đưa bạn đến 10.5GB, giả sử bạn có không gian đó trên hệ thống của mình và JVM có thể lấy nó.
Câu trả lời dài:
Lỗi này khá tự mô tả. Bạn nhấn giới hạn bộ nhớ thực tế trên cấu hình của bạn. Có rất nhiều suy đoán về giới hạn mà bạn có thể có với JVM, tôi không biết đủ về điều đó, vì tôi không thể tìm thấy bất kỳ thông tin chính thức nào. Tuy nhiên, bằng cách nào đó, bạn sẽ bị hạn chế bởi các ràng buộc như trao đổi có sẵn, sử dụng không gian địa chỉ hạt nhân, phân mảnh bộ nhớ, v.v.
Điều đang xảy ra là ByteArrayOutputStream
đối tượng được tạo với bộ đệm mặc định là 32 nếu bạn làm không cung cấp bất kỳ kích thước (đây là trường hợp của bạn). Bất cứ khi nào bạn gọi phương thức write
trên đối tượng, có một máy móc nội bộ được khởi động. Các openjdk implementation release 7u40-b43 mà dường như phù hợp hoàn hảo với đầu ra của lỗi của bạn, sử dụng một phương pháp nội bộ ensureCapacity
để kiểm tra xem bộ đệm có đủ chỗ để đặt các byte bạn muốn viết. Nếu không có đủ chỗ, một phương pháp bên trong khác là grow
được gọi để tăng kích thước của bộ đệm. Phương thức grow
xác định kích thước phù hợp và gọi phương thức copyOf
từ lớp Arrays
để thực hiện công việc. Kích thước thích hợp của bộ đệm là kích thước tối đa giữa kích thước hiện tại và kích thước được đặt để giữ tất cả nội dung (nội dung hiện tại và nội dung mới cần ghi). Phương thức copyOf
từ lớp Arrays
(follow the link) cấp không gian cho bộ đệm mới, sao chép nội dung của bộ đệm cũ sang bộ đệm mới và trả về grow
.
Sự cố của bạn xảy ra khi phân bổ không gian cho bộ đệm mới, Sau một số write
, bạn đã đến một điểm khi bộ nhớ khả dụng bị cạn kiệt: java.lang.OutOfMemoryError: Java heap space
.
Nếu chúng ta nhìn vào chi tiết, bạn đang đọc bởi khối 2048. Vì vậy,
- viết đầu tiên của mình vào phát triển kích thước của bộ đệm 32-2048
- cuộc gọi thứ hai của bạn sẽ tăng gấp đôi nó để 2 * 2048
- cuộc gọi thứ ba của bạn sẽ mang đến 2^2 * 2048, bạn phải dành thời gian viết thêm hai lần nữa trước khi cần phân bổ.
- sau đó 2^3 * 2048, bạn sẽ có thời gian cho 4 mores viết trước khi phân bổ lại.
- tại một số điểm, bộ đệm của bạn sẽ có kích thước 2^18 * 2048 là 2^19 * 1024 hoặc 2^9 * 2^20 (512 MB)
- rồi 2^19 * 2048 là 1024 MB hoặc 1 GB
Có điều gì đó không rõ ràng trong mô tả của bạn là bạn bằng cách nào đó có thể đọc tới 800MB nhưng không thể vượt ra ngoài. Bạn phải giải thích điều đó với tôi.
Tôi hy vọng giới hạn của bạn chính xác là 2 (hoặc đóng nếu chúng tôi sử dụng sức mạnh của 10 đơn vị một cách khác). Về vấn đề đó, tôi hy vọng bạn bắt đầu gặp sự cố ngay lập tức trên một trong các điều sau: 256MB, 512 MB, 1GB, 2GB, v.v.
Khi bạn đạt đến giới hạn đó, điều đó không có nghĩa là bạn đã hết bộ nhớ chỉ đơn giản có nghĩa là không thể phân bổ bộ đệm khác gấp đôi kích thước của bộ đệm mà bạn đã có. Quan sát này mở chỗ cho sự cải tiến trong công việc của bạn: tìm ra kích thước tối đa của bộ đệm mà bạn có thể phân bổ và dự trữ nó trả trước bằng cách gọi các nhà xây dựng phù hợp
ByteArrayOutputStream bArrStream = new ByteArrayOutputStream(myMaxSize);
Nó có lợi thế là giảm cấp phát bộ nhớ nền overhead điều đó xảy ra dưới mui xe để giữ cho bạn hạnh phúc. Bằng cách này, bạn sẽ có thể đi tới giới hạn 1.5 bạn có ngay bây giờ. Điều này đơn giản là vì lần cuối cùng bộ đệm được tăng lên, nó đã đi từ một nửa kích thước hiện tại đến kích thước hiện tại, và tại một số điểm bạn có cả bộ đệm hiện tại và bộ đệm cũ cùng nhau trong bộ nhớ. Nhưng bạn sẽ không thể vượt quá 3 lần giới hạn mà bạn đang có bây giờ. Lời giải thích hoàn toàn giống nhau.
Điều đó đã được nói, tôi không có bất kỳ đề xuất kỳ diệu nào để giải quyết vấn đề ngoài quá trình xử lý dữ liệu của bạn theo từng kích thước nhất định, một đoạn tại một thời điểm. Một cách tiếp cận tốt khác là sử dụng đề xuất của Takahiko Kawasaki và sử dụng MappedByteBuffer
. Hãy nhớ rằng trong mọi trường hợp, bạn sẽ cần ít nhất 10 GB bộ nhớ vật lý hoặc bộ nhớ hoán đổi để có thể tải một tệp có dung lượng 10GB.
xem
Trong mã mẫu bạn đã đề cập, bạn chỉ cần tải toàn bộ tệp trong 'ByteArrayOutputStream'. Trường hợp sử dụng là gì? Bạn có thực sự cần toàn bộ dữ liệu tệp trong một 'byte []' không? – Santosh
Bạn có thể cho tôi biết bạn đang sử dụng phiên bản JDK nào không, tôi có giải pháp khác cho JDK 8 và JDK7 hoặc thấp hơn. – Bhupi
@Làm thế nào để trả lời câu hỏi này mà không biết ** tại sao ** quá nhiều dữ liệu được đọc vào bộ nhớ? – k3b