Java中如何将超长字符串写入GZIP文件?解决内存溢出问题
解决超大字符串写入GZIP文件的内存溢出问题
你遇到的OutOfMemoryError是因为直接调用content.getBytes()会把整个超长字符串一次性转换成字节数组,当字符串足够大时,这个数组的尺寸会超过JVM允许的最大数组大小,直接触发内存溢出。
解决的核心思路是分块处理字符串,不要一次性把整个字符串加载到内存里转为字节数组,而是逐段读取、逐段写入GZIP流。
修改后的代码示例
方法1:基于字符流分块写入
对原way1改造,用StringReader逐块读取字符串,再把每一小块转为字节写入GZIP流:
import java.io.BufferedReader; import java.io.StringReader; import java.nio.CharBuffer; import java.nio.charset.StandardCharsets; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.zip.GZIPOutputStream; public static void way1Fixed() throws IOException { String filePath = "foo"; String content = "very large string"; int chunkSize = 8192; // 8KB的块大小,可根据内存情况调整 char[] buffer = new char[chunkSize]; try (OutputStream os = Files.newOutputStream(Paths.get(filePath)); GZIPOutputStream gos = new GZIPOutputStream(os); BufferedReader reader = new BufferedReader(new StringReader(content))) { int charsRead; while ((charsRead = reader.read(buffer)) != -1) { // 将当前读取到的字符块转为字节数组写入 byte[] chunkBytes = StandardCharsets.UTF_8.encode(CharBuffer.wrap(buffer, 0, charsRead)).array(); gos.write(chunkBytes, 0, chunkBytes.length); } gos.finish(); // 确保GZIP完成所有压缩数据的写入 } }
方法2:基于NIO通道的分块编码
对原way2改造,用CharsetEncoder逐块将字符编码为字节,再写入通道:
import java.nio.charset.StandardCharsets; import java.nio.charset.CharsetEncoder; import java.nio.CharBuffer; import java.nio.ByteBuffer; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.zip.GZIPOutputStream; import java.nio.channels.Channels; import java.nio.channels.WritableByteChannel; import java.io.BufferedReader; import java.io.StringReader; public static void way2Fixed() throws IOException { String filePath = "foo"; String content = "very large string"; int chunkSize = 8192; char[] charBuffer = new char[chunkSize]; CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder(); try (OutputStream os = Files.newOutputStream(Paths.get(filePath)); GZIPOutputStream gos = new GZIPOutputStream(os); WritableByteChannel channel = Channels.newChannel(gos); BufferedReader reader = new BufferedReader(new StringReader(content))) { int charsRead; while ((charsRead = reader.read(charBuffer)) != -1) { CharBuffer cb = CharBuffer.wrap(charBuffer, 0, charsRead); ByteBuffer bb = encoder.encode(cb); channel.write(bb); encoder.reset(); // 重置编码器,准备处理下一块字符 } gos.finish(); } }
注意点
- 块大小(比如8192)可以根据你的JVM内存配置调整,太大可能还是会有内存压力,太小则会增加IO操作次数,找个平衡值即可。
- 一定要调用
GZIPOutputStream.finish(),否则最后一部分压缩数据可能不会写入文件,导致GZIP文件损坏。 - 保持
try-with-resources的用法,确保所有流和通道自动关闭,避免资源泄漏。
内容的提问来源于stack exchange,提问作者Shigure
相关产品推荐
相关产品推荐

