Java文件下載ZIP報(bào)錯(cuò):Out of Memory的問(wèn)題排查
廢話(huà)少說(shuō),上干貨??!
項(xiàng)目中下載小文件或者下載成ZIP文件都正常,但下載超過(guò)2G的ZIP文件,報(bào)錯(cuò):Out of Memory:Java Heap Space。
內(nèi)存溢出,可是大毛病,排查一下代碼:
private void setByteArrayOutputStream(String fileName, InputStream inputStream, ZipArchiveOutputStream zous) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = inputStream.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
baos.flush();
byte[] bytes = baos.toByteArray();
//設(shè)置文件名
ArchiveEntry entry = new ZipArchiveEntry(fileName);
zous.putArchiveEntry(entry);
zous.write(bytes);
zous.closeArchiveEntry();
baos.close();
}細(xì)心的童鞋應(yīng)該已經(jīng)發(fā)現(xiàn)錯(cuò)誤了!
一、原因分析
原因分析主要與 ByteArrayOutputStream 的使用有關(guān)。ByteArrayOutputStream 會(huì)在內(nèi)存中動(dòng)態(tài)擴(kuò)展其緩沖區(qū)以容納寫(xiě)入的數(shù)據(jù)。當(dāng)寫(xiě)入大量數(shù)據(jù)時(shí),尤其是在處理大文件時(shí),可能會(huì)導(dǎo)致內(nèi)存頻繁分配和復(fù)制,從而消耗大量?jī)?nèi)存。大文件處理:如果輸入流(InputStream)讀取的數(shù)據(jù)量很大(如超過(guò)幾百M(fèi)B或GB),ByteArrayOutputStream 可能會(huì)消耗超過(guò)可用內(nèi)存的資源,導(dǎo)致 OutOfMemoryError。
在上述代碼中,邏輯如下:
(1)讀取數(shù)據(jù):通過(guò) inputStream.read(buffer) 持續(xù)將數(shù)據(jù)讀取到緩沖區(qū) buffer 中。
(2)寫(xiě)入 ByteArrayOutputStream:每次讀取的數(shù)據(jù)都被寫(xiě)入到 ByteArrayOutputStream 中。如果文件非常大,ByteArrayOutputStream 將不斷擴(kuò)展其內(nèi)部數(shù)組。
(3) 轉(zhuǎn)換為字節(jié)數(shù)組:調(diào)用 baos.toByteArray() 時(shí),會(huì)創(chuàng)建一個(gè)新的字節(jié)數(shù)組并將所有數(shù)據(jù)復(fù)制到這個(gè)新數(shù)組中,這又需要額外的內(nèi)存。
二、解決方案
采用流式處理:直接從 InputStream 讀取并寫(xiě)入到 ZipArchiveOutputStream,而不使用 ByteArrayOutputStream。這樣可以避免將整個(gè)文件加載到內(nèi)存中。
代碼如下:
?private void setByteArrayOutputStream(String fileName, InputStream inputStream, ZipArchiveOutputStream zous) {
//創(chuàng)建Zip入口
try {
ZipArchiveEntry entry = new ZipArchiveEntry(fileName);
zous.putArchiveEntry(entry);
//使用流緩沖區(qū),一次1024字節(jié),分塊讀取并寫(xiě)入ZIP輸出流
byte[] buffer = new byte[1024];
int len;
while ((len = inputStream.read(buffer)) != -1) {
zous.write(buffer, 0, len);
}
//完成當(dāng)前ZIP入口
zous.closeArchiveEntry();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
//確保輸入流被關(guān)閉
try {
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}運(yùn)行之后,我們發(fā)現(xiàn):不論多大的文件下載,不再報(bào)錯(cuò)內(nèi)存溢出了。但對(duì)于超過(guò)了4G的文件,在形成ZIP包時(shí),出現(xiàn)了其他錯(cuò)誤,如下:
org.apache.commons.compress.archivers.zip.Zip64RequiredException: XXXXXXXXXXXXXXXXXXXXXXXXXXXX's size exceeds the limit of 4GByte. at org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream.checkIfNeedsZip64(ZipArchiveOutputStream.java:651) at org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream.handleSizesAndCrc(ZipArchiveOutputStream.java:638) at org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream.closeArchiveEntry(ZipArchiveOutputStream.java:513)
原因分析:
這個(gè)異常通常是在嘗試創(chuàng)建一個(gè) ZIP 文件時(shí)遇到的,特別是當(dāng)文件的大小超過(guò) 4GB 時(shí)。ZIP 格式的標(biāo)準(zhǔn)限制了單個(gè)文件的大小為 4GB,因此在處理大文件時(shí)需要使用 ZIP64 格式。我們?cè)诖a里添加如下:
zous.setUseZip64(ZipArchiveOutputStream.Zip64Mode.Always); // 啟用 ZIP64 支持 或者(不同版本,函數(shù)不一樣) zous.setUseZip64(Zip64Mode.Always);
按照以上改完之后,我們發(fā)現(xiàn)文件下載ZIP完全沒(méi)有內(nèi)存溢出錯(cuò)誤了。
到此這篇關(guān)于Java文件下載ZIP報(bào)錯(cuò):Out of Memory的問(wèn)題排查的文章就介紹到這了,更多相關(guān)Java文件下載ZIP報(bào)錯(cuò)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- IDEA報(bào)錯(cuò)java.lang.OutOfMemoryError:Java?heap?space的解決辦法
- IDEA報(bào)java:?java.lang.OutOfMemoryError:?Java?heap?space錯(cuò)誤解決辦法
- 解決idea出現(xiàn)的java.lang.OutOfMemoryError:?Java?heap?space的問(wèn)題
- 淺談Java中OutOfMemoryError問(wèn)題產(chǎn)生原因
- Java實(shí)戰(zhàn)之OutOfMemoryError異常問(wèn)題及解決方法
- java.lang.OutOfMemoryError: Metaspace異常解決的方法
- 實(shí)例解決Java異常之OutOfMemoryError的問(wèn)題
- Java中內(nèi)存異常StackOverflowError與OutOfMemoryError詳解
- 完美解決java.lang.OutOfMemoryError處理錯(cuò)誤的問(wèn)題
- java.lang.OutOfMemoryError 錯(cuò)誤整理及解決辦法
- 解決Java中OutOfMemoryError的問(wèn)題
相關(guān)文章
java并發(fā)無(wú)鎖多線(xiàn)程單線(xiàn)程示例詳解
這篇文章主要為大家介紹了java并發(fā)無(wú)鎖多線(xiàn)程單線(xiàn)程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07
關(guān)于IntelliJ IDEA 打包代碼報(bào)錯(cuò)的問(wèn)題
這篇文章主要介紹了關(guān)于IntelliJ IDEA 打包代碼報(bào)錯(cuò)的問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03
spring源碼閱讀--aop實(shí)現(xiàn)原理講解
這篇文章主要介紹了spring源碼閱讀--aop實(shí)現(xiàn)原理講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09
詳解MyBatis動(dòng)態(tài)代理機(jī)制
MyBatis的動(dòng)態(tài)代理機(jī)制是其核心特性之一,通過(guò)該機(jī)制,開(kāi)發(fā)者可以專(zhuān)注于定義接口和 SQL 映射,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2026-03-03
使用kafka-console-consumer.sh不停報(bào)WARN的問(wèn)題及解決
這篇文章主要介紹了使用kafka-console-consumer.sh不停報(bào)WARN的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
Java實(shí)現(xiàn)十秒向MySQL插入百萬(wàn)條數(shù)據(jù)
這篇文章主要為大家詳細(xì)介紹了Java如何實(shí)現(xiàn)十秒向MySQL插入百萬(wàn)條數(shù)據(jù),文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)或工作有一定借鑒價(jià)值,需要的可以參考一下2022-11-11
Java二分查找算法與數(shù)組處理的應(yīng)用實(shí)例
二分查找法,又叫做折半查找法,它是一種效率較高的查找方法。數(shù)組對(duì)于每一門(mén)編程語(yǔ)言來(lái)說(shuō)都是重要的數(shù)據(jù)結(jié)構(gòu)之一,當(dāng)然不同語(yǔ)言對(duì)數(shù)組的實(shí)現(xiàn)及處理也不盡相同。Java 語(yǔ)言中提供的數(shù)組是用來(lái)存儲(chǔ)固定大小的同類(lèi)型元素2022-07-07
SpringBoot+EasyExcel實(shí)現(xiàn)自定義復(fù)雜樣式導(dǎo)入導(dǎo)出
這篇文章主要為大家詳細(xì)介紹了SpringBoot如何結(jié)果EasyExcel實(shí)現(xiàn)自定義復(fù)雜樣式導(dǎo)入導(dǎo)出功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下2025-07-07
Java實(shí)現(xiàn)百萬(wàn)數(shù)據(jù)分批次插入的最佳實(shí)踐分享
在當(dāng)今數(shù)據(jù)驅(qū)動(dòng)的應(yīng)用開(kāi)發(fā)中,處理大批量數(shù)據(jù)插入是后端開(kāi)發(fā)人員經(jīng)常面臨的挑戰(zhàn),隨著業(yè)務(wù)規(guī)模的擴(kuò)大,數(shù)據(jù)量呈現(xiàn)爆發(fā)式增長(zhǎng),傳統(tǒng)單條插入方式在面對(duì)百萬(wàn)級(jí)數(shù)據(jù)時(shí)顯得力不從心,所以本文給大家分享了Java實(shí)現(xiàn)百萬(wàn)數(shù)據(jù)分批次插入的最佳實(shí)踐,需要的朋友可以參考下2025-07-07

