【问题标题】:Out of Memory Error Failing on String字符串上的内存不足错误失败
【发布时间】:2014-12-08 19:59:57
【问题描述】:

我有一个循环可以循环数十次,用于债务偿还计算器。

这个循环所做的许多事情之一就是调用一个名为getSpecialPayment的方法

  private double getSpecialPayment(long debt_id, int month) {

    int thisMonth = Calendar.getInstance().get(Calendar.MONTH) + 1;

    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.MONTH, month + thisMonth);

    SimpleDateFormat df = new SimpleDateFormat("MMM yyyy", Locale.ENGLISH);
    String sMonth = df.format(cal.getTime());

    // Line 884 that fails below 
    String check_for_special = "SELECT payment FROM special_payments WHERE id = " + debt_id + " and month = '" + sMonth + "' LIMIT 1;"; 

    if (!database.isOpen()) {
        open();
    }

    Cursor c = database.rawQuery(check_for_special, null);
    if (c.getCount() == 0) {
        c.close();
        return 0;
    } else {
        c.moveToFirst();
        double amount = c.getDouble(c.getColumnIndex("payment"));
        c.close();
        return amount;
    }
}

我在构建字符串(查询)check_for_special 的行上得到了这个 OutOfMemory 堆栈跟踪。

java.lang.RuntimeException: An error occured while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:300)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:841)
Caused by: java.lang.OutOfMemoryError
at java.lang.AbstractStringBuilder.enlargeBuffer(AbstractStringBuilder.java:94)
at java.lang.AbstractStringBuilder.append0(AbstractStringBuilder.java:145)
at java.lang.StringBuilder.append(StringBuilder.java:216)
at com.---.---.DebtDataSource.getSpecialPayment(DebtDataSource.java:884)
at com.---.---.DebtDataSource.payoffDebt(DebtDataSource.java:469)
at com.---.---.PlannerFragment$PlannerTask.doInBackground(PlannerFragment.java:156)
at com.---.---.PlannerFragment$PlannerTask.doInBackground(PlannerFragment.java:122)
at android.os.AsyncTask$2.call(AsyncTask.java:288)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
... 4 more

我不明白为什么String 初始化会导致失败;或者这是一个更大的问题在这一点上失败的巧合?

编辑:

这张照片是我的堆转储:

【问题讨论】:

    标签: java android out-of-memory


    【解决方案1】:

    这里有一个更大的问题。由getSpecialPayment 引起的OOM 错误是内存泄漏到别处的症状。

    一些上下文,这里是这一行

    String check_for_special = "SELECT payment FROM special_payments WHERE id = " + debt_id + " and month = '" + sMonth + "' LIMIT 1;";
    

    真的会做类似的事情

    StringBuilder builder = new StringBuilder("SELECT payment FROM special_payments WHERE id = ");
    builder.append(debt_id);
    builder.append("and month = '");
    builder.append(sMonth);
    builder.append(' LIMIT 1;");
    

    所有这些都被编译掉了,所以如果不单步执行代码,您就看不到它。

    这告诉你,你正在用掉太多没有被回收的内存,以至于当你尝试做一些像这样简单而低内存的事情时,它就会耗尽内存。

    我建议创建一个堆转储并尝试找出最大的内存违规者。

    【讨论】:

    • 同意,另外仔细查看在此函数中创建的任何对象(称为“数十次”)。例如,CalendarInstance 和 SimpleDateFormat 可以创建一次并重复使用。另外,考虑一个参数化查询而不是来自字符串的 rawQuery(例如stackoverflow.com/questions/9857073/…),这是一个只有参数变化的单个查询字符串
    • 好的,谢谢。猜猜我将不得不追查这个并学习如何使用内存监视器工具。
    • @KickingLettuce 我强烈推荐 Eclipse MAT。它真的很容易使用。 jmap 是我用于堆转储的。
    • @JohnVint 我安装了 android studio,而不是 Eclipse。不是有只能为 DDMS 下载的 MAT 插件吗?
    • @KickingLettuce 你能得到堆转储吗?无论是通过jmap还是其他方式?如果是这种情况,您可以将 Eclipse MAT 作为独立应用程序运行,并将 Java 二进制堆转储作为参数。所以不需要和android有关联。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-14
    • 1970-01-01
    • 2014-01-06
    • 2021-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多