Android中出现OutOfMemory错误。 Android如何为应用分配内存?

时间:2016-01-07 10:05:40

标签: android

我正在解析从互联网上获取的JSON字符串(单个请求中大约有40,000条记录)。我递归调用AsyncTask,直到从服务器获取所有记录(总记录200k)。

在前两个调用应用程序中,响应平稳,从服务器获取大约80,000条记录,但在第三次调用中发生OutOfMemoryException。我在网上搜索并知道当堆大小溢出时会发生这个错误。

String response = "";
            try {
                DefaultHttpClient httpClient = new DefaultHttpClient();
                HttpEntity httpEntity = null;
                HttpResponse httpResponse = null;

                HttpPost httpPost = new HttpPost("http://URL");                                

                httpPost.setEntity(new StringEntity(strings[0]));

                httpPost.setHeader("Content-Type", "application/json");
                httpPost.setHeader("Accept", "application/json");


                httpResponse = httpClient.execute(httpPost);
                httpEntity = httpResponse.getEntity();
                response = EntityUtils.toString(httpEntity);

错误发生在下面一行。

response = EntityUtils.toString(httpEntity);

我的代码适用于前2个调用,那为什么它会在下一个调用中抛出OutOfMemoryExcption?为什么前两个调用使用相同大小的数据?

我知道有很多关于OutOfMemoryException的问题,但我想知道 Android如何分配堆的大小是否取决于设备的可用内存当应用数据的大小增加或者保持相同时,堆的大小是否会减少

我的代码:

public void startBackgrounSync(){

            final JSONObject jsonObject = new JSONObject();
    try{
        jsonObject.put("token","dssdf");
        jsonObject.put("api_key","fdsf");
        jsonObject.put("org_id","101");
        jsonObject.put("sync_type","background_sync");
        jsonObject.put("start",Integer.toString(start));
        jsonObject.put("total",Integer.toString(limit));
    }catch (JSONException e){
        e.printStackTrace();
    }

    AsyncTask<String, Integer, String> backGroundSync = new AsyncTask<String, Integer, String>() {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();

        }

        @Override
        protected String doInBackground(String... strings) {
            System.out.println("BackGround Sync Process Start: ");

            String response = "";
            try {
                DefaultHttpClient httpClient = new DefaultHttpClient();
                HttpEntity httpEntity = null;
                HttpResponse httpResponse = null;
                HttpPost httpPost = new HttpPost("http://URL");

                httpPost.setEntity(new StringEntity(strings[0]));

                httpPost.setHeader("Content-Type", "application/json");
                httpPost.setHeader("Accept", "application/json");


                httpResponse = httpClient.execute(httpPost);


                httpEntity = httpResponse.getEntity();
                response = EntityUtils.toString(httpEntity);


            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } catch (ClientProtocolException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            Log.d("Response: ", "> " + response);

            return response;
        }

        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);

                try{
                    JSONObject jsonObject1 = new JSONObject(s);
                    JSONObject data = jsonObject1.getJSONObject("data");

                    if(data != null){

                        if (data.getBoolean("is_success")){
                            JSONObject alumni_data = data.getJSONObject("alumni_data");

                                JSONArray alumni_profile_data = alumni_data
                                        .getJSONArray("alumni_profile_data");

    ContentValues[] values = new ContentValues[alumni_profile_data.length()];

                            if (alumni_profile_data == null){
                                Toast.makeText(MainActivity.this,"Sync complete.....",Toast.LENGTH_LONG).show();

                            }else{
                                JSONObject alumini_object;
                                for (int i = 0; i < alumni_profile_data.length(); i++) {
                                    alumini_object = alumni_profile_data.getJSONObject(i);

                                    String id = alumini_object.getString("id");
                                    String org_id = alumini_object.optString("org_id");
                                                                      ContentValues  contentValues = new ContentValues();

                                    contentValues.put("id", id);
                                    contentValues.put("org_id", org_id);
                                    contentValues.put("profile_photo", profile_photo);


                                    values[i] = contentValues;

                                }
                            }

                            String uri = "content://com.poras.provider.alumni/data";

                            Uri uri1 = Uri.parse(uri);

                            int length = getApplicationContext().getContentResolver().bulkInsert(uri1,values);
                            System.out.println("Length of rows...... " +length);


                            start = start+50000;
                            values = new ContentValues[0];

                            startBackgrounSync();// Calling Method Recursively

                        }
                    }

                }catch (JSONException e){
                    e.printStackTrace();
                }catch (OutOfMemoryError error){
                    System.out.println("Out of memory exception...... "+ error);
                }

            }

        }
    };
    backGroundSync.execute(jsonObject.toString());

}

3 个答案:

答案 0 :(得分:3)

您的问题表明您正在将这些方法称为递归 - 而且这很可能会导致OutOfMemoryException

当您调用方法时,Java会将该方法的对象存储在堆空间中,并且这些值仅在方法返回后由垃圾回收器回收。以递归方式调用方法意味着该方法将多次执行,一直分配给堆,并导致长线程堆栈。

简单的解决方案是不以递归方式调用Async,而是在onPostExecute方法中开始下一个调用(假设您正在使用AsyncTask

我个人完全避免使用android:largeHeap="true"作为文档明智地陈述:

  

永远不要仅仅因为内存耗尽而需要快速修复而请求大堆 - 只有当您确切知道所有内存的分配位置以及必须保留的原因时才应使用它。 / p>

就Android如何为应用程序分配堆空间而言,docs状态

  

根据设备总体可用RAM的大小,设备之间的确切堆大小限制会有所不同。如果您的应用已达到堆容量并尝试分配更多内存,则会收到OutOfMemoryError

Activity您可以使用getMemoryClass()查询设备当前为应用分配了多少堆空间

  

返回当前设备的近似每应用程序内存类。

答案 1 :(得分:0)

OutOfMemory Exception

  

在发出无法满足的内存请求时抛出   使用可用的平台资源。这样的请求可以由   运行的应用程序或VM的内部功能。

您可以在清单中设置android:largeHeap="true" [非常规解决方案]

  

是否应该使用大型创建应用程序的进程   达尔维克堆。这适用于为此创建的所有进程   应用。它只适用于第一个加载到的应用程序   处理;如果您使用共享用户ID来允许多个   要使用进程的应用程序,它们都必须使用此选项   一直或他们将有不可预测的结果。

因此,为了维持功能强大的多任务环境,Android为每个应用设置了堆大小的硬限制。根据设备可用的RAM总量,设备之间的确切堆大小限制会有所不同。如果您的应用已达到堆容量并尝试分配更多内存,则会收到 OutOfMemoryError

您可以拨打 getMemoryClass() 来估算应用的可用堆(以兆字节为单位)。如果您的应用尝试分配的内存超过此处可用内存,则会收到 OutOfMemoryError

阅读 Check how much memory you should use 。希望这会有所帮助。

答案 2 :(得分:0)

如上所述here

  Linux等现代操作系统上的内存使用情况非常严重   复杂且难以理解的领域

ActivityManager.getMemoryClass()为您提供每个应用每个应用的估算内存。

我得到OutOfMemoryException您可能做错了,您应该分析内存使用情况,如Investigating Your RAM Usage中所述,或者更具体地说是Android Studio Memory and CPU monitor

在你的情况下,我认为递归处理大量数据是一个非常糟糕的主意,例如,它可以很容易地在调用堆栈的每一步维护对大对象的引用,从而禁止它们进行垃圾回收。