OutOfMemoryError:分页响应时分配失败

Posted

技术标签:

【中文标题】OutOfMemoryError:分页响应时分配失败【英文标题】:OutOfMemoryError: Failed to allocate when paging through responses 【发布时间】:2021-12-24 09:18:48 【问题描述】:

我收到以下 OutOfMemoryError 异常尝试通过 API 的响应分页

原因:java.lang.OutOfMemoryError:无法分配 37748744 字节分配,25165824 空闲字节和 24MB 直到 OOM,目标占用空间 268261936,增长限制 268435456 在 java.util.Arrays.copyOf(Arrays.java:3257) 在 java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124) 在 java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:649) 在 java.lang.StringBuilder.append(StringBuilder.java:203) 在 com.richmondgamesstudio.inattaxonomytreeviewer.utils.INatCall.readAll(INatCall.java:105) 在 com.richmondgamesstudio.inattaxonomytreeviewer.utils.INatCall.restCalliNat(INatCall.java:56) 在 com.richmondgamesstudio.inattaxonomytreeviewer.utils.INatCall.restCalliNat(INatCall.java:75) 在 com.richmondgamesstudio.inattaxonomytreeviewer.utils.INatCall.restCalliNat(INatCall.java:75) 在 com.richmondgamesstudio.inattaxonomytreeviewer.utils.INatCall.restCalliNat(INatCall.java:75) 在 com.richmondgamesstudio.inattaxonomytreeviewer.utils.INatCall.doInBackground(INatCall.java:42)

有没有更有效的方法通过休息来处理大量数据? 我的应用程序调用返回用户“条目”的 API。 API 的最大页数为 200。大多数用户将拥有超过 200 个条目,这在大多数情况下都很好。我的应用程序可以分页并说明这一点。但是,有些用户将拥有 +2000 个条目,而我的应用程序在尝试遍历这些更大的集合时内存不足。 我的问题是:

有没有办法增加我的 android 应用程序可以使用的内存量 用吗? 有哪些方法可以优化以下代码以减少使用 记忆力?

递归休息调用

   private JSONArray restCall(URL url, JSONArray results) 
        Log.d(TAG, "restCalliNat: Start");
        InputStream is = null;
        int totalResults = 0;
        int perPage = 0;
        JSONArray newResults = new JSONArray();
        try 
            is = url.openStream();
            BufferedReader rd = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8")));
            String jsonText = readAll(rd); //<-- LINE 56
            is.close();
            rd.close();
            JSONObject json = new JSONObject(jsonText);
            newResults = json.getJSONArray("results");
            totalResults = (int) json.get("total_results");
            perPage = (int) json.get("per_page");

         catch (IOException | JSONException e) 
            e.printStackTrace();
            return null;
        

        if (results != null) 
            newResults = concatArray(results, newResults);
        

        if (totalResults > page*perPage) 

            newResults = restCall(updatePageCount(url), newResults);
        
        return newResults;
    

在每个新页面上,新页面都会连接起来,直到我拥有所有条目为止。

private JSONArray concatArray(JSONArray arr1, JSONArray arr2) 
    JSONArray result = new JSONArray();
    try 
        for (int i = 0; i < arr1.length(); i++) 
            result.put(arr1.get(i));
        
        for (int i = 0; i < arr2.length(); i++) 
            result.put(arr2.get(i));
        
     catch (JSONException e) 
        e.printStackTrace();
    
    return result;

将 API 响应转换为字符串

private static String readAll(Reader rd) throws IOException 
    StringBuilder sb = new StringBuilder();
    int cp;
    while ((cp = rd.read()) != -1) 
        sb.append((char) cp); //<--- LINE 105
    
    return sb.toString();

【问题讨论】:

【参考方案1】:

在你的清单中添加 android:hardwareAccelerated="false" , android:largeHeap="true" 它在某些情况下工作,

<application
    android:allowBackup="true"
    android:hardwareAccelerated="false"
    android:largeHeap="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">

【讨论】:

【参考方案2】:

原来我所调用的权限对您可以请求的页面数量有限制。

每页计数限制为 200 个页面结果。但是 page_count 限制是 page * per_Page > 1000。所以 per_page 计数是多少并不重要。如果 page * per_Page > 1000 则将取消调用。即使在溪流中间。

【讨论】:

【参考方案3】:

我的应用程序内存不足

这并不是错误所说的:Caused by: java.lang.OutOfMemoryError: Failed to allocate a 37748744 byte allocation with 25165824 free bytes and 24MB until OOM。您有 24MB 的可用内存。但是,您尝试分配一个 36MB 的单个缓冲区,但这是行不通的。即使您有 124MB 或 524MB 的可用内存,您也可能无法访问单个 36MB 的连续可用内存块。

将 API 响应转换为字符串

要么:

一次从您的 REST Web 服务请求更少的项目(而不是 200 个,尝试 5 个);或 停止尝试将整个 API 响应读入字符串,因为您的 API 响应非常大

您可能想尝试更现代的解决方案来访问 REST 样式的 Web 服务。例如,Retrofit 非常流行,并且不需要您将整个原始 JSON 读入单个字符串。即使您想坚持使用 openStream()-on-a-URL 方法,现代 JSON 解析器(Moshi、Gson,甚至 Android SDK 中的 JsonReader)都是流式解析器,不需要您阅读将整个原始 JSON 转换为单个字符串。

【讨论】:

以上是关于OutOfMemoryError:分页响应时分配失败的主要内容,如果未能解决你的问题,请参考以下文章

采用JQuery实现的打印HTML表格自动按多少行分页,打印时分页

返回数组 Api 资源对象时分页

eclipse 运行报java.lang.OutOfMemoryError: PermGen space解决方法

React Apollo Client:点击按钮时分页和获取数据

在 flexdashboard 中添加下载处理程序时分页被切断

java.lang.OutOfMemoryError: unable to create new native thread