You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用流式GSON结合ByteArrayOutputStream生成JSON字符串时遇OutOfMemoryError

优化方案:解决序列化大列表时的OutOfMemoryError问题

看来你遇到了个头疼的问题——原本想修复OOM崩溃,结果改完之后问题反而更频繁,还没法本地复现,确实棘手。咱们先拆解下当前代码的问题,再给你几个进阶优化方案:

问题根源分析

你当前的代码用ByteArrayOutputStream在内存中缓存整个JSON数组的字节数据,最后转成String的时候,还要额外分配一份和缓冲区大小一致的内存(因为String是不可变的)。当BlaModel列表很大时,这个内存开销会急剧膨胀,在内存紧张的设备上直接触发OOM。而你之前尝试的StringBuilder方案,本质也是在内存中攒整个字符串,内存占用只会更高。

进阶优化方案

1. 直接写入文件(推荐,彻底规避内存缓冲)

如果你的最终目的是保存数据,SharedPreferences并不适合存储大体积JSON——它设计来存小配置项的。更合理的做法是直接把JSON序列化到文件,完全跳过内存缓冲区:

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import com.google.gson.Gson;
import com.google.gson.stream.JsonWriter;

// 全局复用Gson实例(Gson是线程安全的)
private static final Gson GSON = new Gson();

private static boolean saveBlaModelsToFile(List<BlaModel> blaModels, File targetFile) {
    try (FileOutputStream fos = new FileOutputStream(targetFile);
         OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
         JsonWriter writer = new JsonWriter(osw)) {
        
        writer.setIndent(" ");
        writer.beginArray();
        for (BlaModel model : blaModels) {
            // 直接把单个模型序列化到文件流,内存只保留当前模型的临时数据
            GSON.toJson(model, BlaModel.class, writer);
        }
        writer.endArray();
        return true;
    } catch (IOException e) {
        // 替换成你项目的日志工具,比如Logcat或者Crashlytics
        e.printStackTrace();
        return false;
    }
}

优势:内存占用极低,只会在序列化单个BlaModel时占用少量内存,完全不会出现大内存块分配的情况。

2. 拆分数据到SharedPreferences(必须用SP时的最优解)

如果业务限制必须用SharedPreferences,那可以把大列表拆成单个模型存储,彻底避免一次性序列化整个列表:

import android.content.SharedPreferences;
import com.google.gson.Gson;

// 全局复用Gson实例
private static final Gson GSON = new Gson();

// 保存拆分后的模型
private static void saveBlaModelsSplit(List<BlaModel> blaModels, SharedPreferences prefs) {
    SharedPreferences.Editor editor = prefs.edit();
    
    // 先保存列表长度
    editor.putInt("bla_models_count", blaModels.size());
    
    // 逐个序列化单个模型并存储
    for (int i = 0; i < blaModels.size(); i++) {
        String modelJson = GSON.toJson(blaModels.get(i), BlaModel.class);
        editor.putString("bla_model_" + i, modelJson);
    }
    
    // 用apply()异步提交,避免阻塞主线程
    editor.apply();
}

// 读取拆分后的模型
private static List<BlaModel> loadBlaModelsSplit(SharedPreferences prefs) {
    int modelCount = prefs.getInt("bla_models_count", 0);
    List<BlaModel> models = new ArrayList<>(modelCount);
    
    for (int i = 0; i < modelCount; i++) {
        String modelJson = prefs.getString("bla_model_" + i, null);
        if (modelJson != null) {
            models.add(GSON.fromJson(modelJson, BlaModel.class));
        }
    }
    return models;
}

优势:每次只处理单个模型,内存占用可控,还能规避SharedPreferences单个条目的大小限制(部分设备对SP单条数据大小有限制)。

3. 优化内存中的序列化(适合列表较小的场景)

如果必须生成完整的JSON字符串,那可以替换ByteArrayOutputStream为CharArrayWriter,减少字节到字符的转换开销:

import java.io.CharArrayWriter;
import com.google.gson.Gson;
import com.google.gson.stream.JsonWriter;

// 全局复用Gson实例
private static final Gson GSON = new Gson();

@Nullable private static String streamBlaModelsIntoJsonString(List<BlaModel> blaModels) {
    try (CharArrayWriter charWriter = new CharArrayWriter();
         JsonWriter writer = new JsonWriter(charWriter)) {
        
        writer.setIndent(" ");
        writer.beginArray();
        for (BlaModel model : blaModels) {
            GSON.toJson(model, BlaModel.class, writer);
        }
        writer.endArray();
        return charWriter.toString();
    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }
}

优化点:CharArrayWriter直接操作字符数组,避免了ByteArrayOutputStream转String时的字节解码操作,减少了一次内存复制和分配。

4. 通用Gson优化建议

  • 复用Gson实例:不要在方法内每次都new Gson(),Gson是线程安全的,可以全局初始化一次,减少对象创建的内存开销。
  • 精简序列化配置:如果不需要HTML转义,可以用new GsonBuilder().disableHtmlEscaping().create(),减少生成的JSON长度,间接降低内存占用。
  • 用TypeAdapter替代反射:如果BlaModel结构复杂,可以自定义TypeAdapter,避免Gson反射带来的额外内存开销(适合性能敏感场景)。

总结

优先推荐方案1(写文件)或者方案2(拆分存储),这两个方案从根源上解决了大内存块分配的问题。如果必须生成完整JSON字符串,再考虑方案3。另外一定要记得复用Gson实例,减少不必要的内存开销。

内容的提问来源于stack exchange,提问作者Egemen Hamutçu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:43:08