new Gson().toJson()导致的oom

文章讲述了作者在启动jar应用时遇到接口超时的问题,起初认为是服务器性能问题,但深入分析后发现是由于大量内存消耗导致的OOM。问题根源在于使用Gson的toJson方法构建响应数据时,内存占用过大。为了解决这个问题,作者采用了fastjson的JSON.toJSONString方法作为替代方案,成功避免了内存溢出。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

每次启动jar,接口都超时,一开始我以为是服务器性能不行,让我很是苦恼。在分析了一波之后,我开始尝试查找具体原因,发现在调用接口之后,内存飙升直到产生了oom。

以下是部分错误日志:

java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3332) ~[na:1.8.0_121]
at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124) ~[na:1.8.0_121]
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448) ~[na:1.8.0_121]
at java.lang.StringBuffer.append(StringBuffer.java:270) ~[na:1.8.0_121]
at java.io.StringWriter.write(StringWriter.java:101) ~[na:1.8.0_121]
at java.io.StringWriter.append(StringWriter.java:143) ~[na:1.8.0_121]
at java.io.StringWriter.append(StringWriter.java:41) ~[na:1.8.0_121]
at com.google.gson.stream.JsonWriter.value(JsonWriter.java:566) ~[gson-2.9.0.jar!/:na]

发现报错位置在copyOf()方法内,如下:

    public static char[] copyOf(char[] original, int newLength) {
        char[] copy = new char[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

可以发现,每次调用copyOf方法时都会new一个char[],我在构建响应数据时使用了new Gson().toJson(),因此,一旦响应数据过多,就可能会造成oom。

网上搜索了一波后,暂时用fastjson的JSON.toJSONString()方法代替可以解决。

package com.example.kucun2.DataPreserver; import android.content.Context; import android.os.Handler; import android.os.Looper; import android.util.Log; import com.example.kucun2.entity.*; import com.example.kucun2.entity.Information; import com.example.kucun2.entity.data.*; import com.example.kucun2.function.MyAppFunction; import com.example.kucun2.function.SafeLogger; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import java.io.IOException; import java.lang.reflect.Type; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.stream.Collectors; import okhttp3.Call; import okhttp3.Callback; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; /** * 数据加载器 - 负责从网络加载数据 */ public class DataLoader { private static final String TAG = "DataLoader"; private final DataStore dataStore; private final DataAssociator dataAssociator; private final DataPreserver dataPreserver; private final OkHttpClient client; public DataLoader(DataStore dataStore, DataAssociator dataAssociator, DataPreserver dataPreserver, OkHttpClient client) { this.dataStore = dataStore; this.dataAssociator = dataAssociator; this.dataPreserver = dataPreserver; this.client = client; } public void loadAllData(Context context, LoadDataCallback callback) { if (Looper.myLooper() != Looper.getMainLooper()) { throw new IllegalStateException("必须在主线程调用Data.loadAllData"); } dataPreserver.ensurePreservedObjects(); // 确保存在预置对象 Handler mainHandler = new Handler(context.getMainLooper()); ExecutorService executor = Executors.newSingleThreadExecutor(); executor.execute(() -> { SynchronizableEntity.setSyncEnabled(false); String url = MyAppFunction.getStringResource("string", "url") + MyAppFunction.getStringResource("string", "url_all"); Request request = new Request.Builder().url(url).build(); Log.d(TAG, "loadAllData: "+url); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Log.e(TAG, "Failed to load data", e); SynchronizableEntity.setSyncEnabled(true); mainHandler.post(() -> callback.onFailure()); } @Override public void onResponse(Call call, Response response) throws IOException { if (!response.isSuccessful()) { Log.e(TAG, "Unexpected response code: " + response.code()); SynchronizableEntity.setSyncEnabled(true); mainHandler.post(() -> callback.onFailure()); return; } String responseData = response.body().string(); SynchronizableEntity.setSyncEnabled(true); dataPreserver.ensurePreservedObjects(); mainHandler.post(() -> { parseAndAssignData(responseData, context, callback); }); } }); }); } private void parseAndAssignData(String jsonData, Context context, LoadDataCallback callback) { try { Type responseType = new TypeToken<Information<AllDataResponse>>() {}.getType(); Information<AllDataResponse> info = new Gson().fromJson(jsonData, responseType); if (info == null || info.getData() == null || info.getStatus() != 200) { throw new IllegalStateException("无效服务器响应"); } AllDataResponse allData = info.getData(); SafeLogger.d("data", "原始数据: " + new Gson().toJson(allData)); updateAllLists(allData); dataAssociator.automaticAssociation(); setAllEntitiesState(SynchronizableEntity.SyncState.MODIFIED); callback.onSuccess(); } catch (Exception e) { Log.e(TAG, "数据处理异常: " + e.getMessage()); callback.onFailure(); } finally { SynchronizableEntity.setSyncEnabled(true); } } private void updateAllLists(AllDataResponse allData) { updateList(dataStore.bancais, allData.bancais); updateList(dataStore.caizhis, allData.caizhis); updateList(dataStore.mupis, allData.mupis); updateList(dataStore.chanpins, allData.chanpins); updateList(dataStore.chanpin_zujians, allData.chanpin_zujians); updateList(dataStore.dingdans, allData.dingdans); updateList(dataStore.dingdan_chanpins, allData.dingdan_chanpins); updateList(dataStore.dingdan_chanpin_zujians, allData.dingdan_chanpin_zujians); updateList(dataStore.kucuns, allData.kucuns); updateList(dataStore.zujians, allData.zujians); updateList(dataStore.users, allData.users); updateList(dataStore.jinhuos, allData.jinhuos); } private <T extends SynchronizableEntity> void updateList( List<T> existingList, List<T> newList) { if (newList == null) return; List<T> preservedItems = existingList.stream() .filter(item -> item != null && item.isPreservedObject()) .collect(Collectors.toList()); existingList.clear(); existingList.addAll(preservedItems); existingList.addAll(newList.stream() .filter(item -> item != null && item.getId() != null && item.getId() != -1) .collect(Collectors.toList()) ); } private void setAllEntitiesState(SynchronizableEntity.SyncState state) { dataStore.dataCollectionMap.values().forEach(list -> list.forEach(entity -> { if (entity != null) entity.setState(state); }) ); } public interface LoadDataCallback { void onSuccess(); void onFailure(); } public static class AllDataResponse { public List<Bancai> bancais; public List<Caizhi> caizhis; public List<Mupi> mupis; public List<Chanpin> chanpins; public List<Chanpin_Zujian> chanpin_zujians; public List<Dingdan> dingdans; public List<Dingdan_Chanpin> dingdan_chanpins; public List<Dingdan_chanpin_zujian> dingdan_chanpin_zujians; public List<Kucun> kucuns; public List<Zujian> zujians; public List<User> users; public List<Jinhuo> jinhuos; } } 加入详细注解
最新发布
06-19
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

向阳不像羊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值