Android 算术计算器中的垃圾收集过多

Android 算术计算器中的垃圾收集过多,android,garbage-collection,Android,Garbage Collection,我正在尝试创建一个Android应用程序,它可以绘制用户输入的简单数学函数(本质上是一个图形计算器)。 每个onDraw调用每秒需要数百次算术求值(这些求值被绘制在屏幕上以生成图形)。当我的代码对表达式求值时,程序的速度会大大减慢;当内置方法对表达式求值时,应用程序运行时不会出现问题 根据LogCat的说法,垃圾收集大约每秒发生12次,每次暂停应用程序大约15毫秒,导致每秒数百毫秒的冻结。我认为这就是问题所在 这是我的evaluator函数的一个提炼版本。要计算的表达式名为“postfixEqu

我正在尝试创建一个Android应用程序,它可以绘制用户输入的简单数学函数(本质上是一个图形计算器)。 每个onDraw调用每秒需要数百次算术求值(这些求值被绘制在屏幕上以生成图形)。当我的代码对表达式求值时,程序的速度会大大减慢;当内置方法对表达式求值时,应用程序运行时不会出现问题

根据LogCat的说法,垃圾收集大约每秒发生12次,每次暂停应用程序大约15毫秒,导致每秒数百毫秒的冻结。我认为这就是问题所在

这是我的evaluator函数的一个提炼版本。要计算的表达式名为“postfixEquation”,字符串ArrayList“list”在过程结束时保存最终答案。还有两个名为“数字”和“运算符”的字符串数组,用于存储能够使用的数字和符号:

String evaluate(String[] postfixEquation) {

    list.clear();

    for (int i = 0; i < postfixEquation.length; i++) {

        symbol = postfixEquation[i];

        // If the first character of our symbol is a digit, our symbol is a numeral
        if (Arrays.asList(digits).contains(Character.toString(symbol.charAt(0)))) {

            list.add(symbol);

            } else if (Arrays.asList(operators).contains(symbol))  {

                // There must be at least 2 numerals to operate on
                if (list.size() < 2) {
                    return "Error, Incorrect operator usage.";
                }

                // Operates on the top two numerals of the list, then removes them 
                // Adds the answer of the operation to the list
                firstItem = Double.parseDouble(list.get(list.size() - 1));
                secondItem = Double.parseDouble(list.get(list.size() - 2));
                list.remove(list.size() - 1);
                list.remove(list.size() - 1);

                if (symbol.equals(operators[0])){ 
                    list.add( Double.toString(secondItem - firstItem) );  
                } else if (symbol.equals(operators[1])) {
                    list.add( Double.toString(secondItem + firstItem) );
                } else if (symbol.equals(operators[2])) {
                    list.add( Double.toString(secondItem * firstItem) );
                } else if (symbol.equals(operators[3])) {
                    if (firstItem != 0) {
                        list.add( Double.toString(secondItem / firstItem) );
                    } else {
                        return "Error, Dividing by 0 is undefined.";
                    }
                } else {
                    return "Error, Unknown symbol '" + symbol + "'.";
                }
            }
        }

    // The list should contain a single item, the final answer 
    if (list.size() != 1) {
        return "Error, " + list has " + list.size() + " items left instead of 1.";
    }

    // All is fine, return the final answer
    return list.get(0);
}
String求值(String[]postfix方程){
list.clear();
for(int i=0;i
操作中使用的数字都是字符串,因为我不确定是否可以在一个数组中保存多个类型(即字符串和Double),因此出现了大量的“Double.parseDouble”和“Double.toString”调用

如何减少此处发生的垃圾收集量?

如果有帮助的话,我一直在使用以下步骤来计算我的后缀表达式:。
我已经好几个星期没有解决这个问题了。如果您能提供任何帮助,我们将不胜感激。谢谢。

可能是您的列表操作导致了这个问题。列表内部有数组,数组会根据列表中的数据量进行扩展/收缩。因此,随机进行大量添加和删除操作将非常需要垃圾收集离子

避免这种情况的解决方案是使用正确的
List
实现。对于您的问题,请在开始时为列表分配足够的空间,以避免调整内部数组的大小,并标记未使用的元素,而不是删除它们

冻结症状是因为您正在您的应用程序中进行计算。如果您不希望您的应用程序冻结,您可能需要在单独的线程上检查以执行计算


PS:看起来你在做一些无用的操作……为什么
parseDouble()
secondItem

Java中的紧循环规则是不分配任何东西。你看到如此频繁的GC收集就是证明

您似乎是在使用
双精度
进行计算,然后将其转换为
字符串
。不要这样做,这对性能很糟糕,因为您创建了大量的字符串,然后将其丢弃(另外,您在字符串和双精度之间来回转换了很多)。只需维护一个
ArrayDeque
,并将其用作堆栈即可--这还可以避免执行可能会降低性能的数组大小调整

预编译输入公式。将所有输入操作转换为
enum
实例——它们比较起来更快(只需使用
开关
语句),甚至可能使用更少的内存。如果您需要处理double,可以使用通用的
对象
容器和
实例
,或者使用包含操作
枚举
double
的容器类。预编译可以避免在紧循环中进行昂贵的测试


如果你做这些事情,你的循环肯定会飞。

在你的UI线程中没有发生15MS停顿,所以它们不应该对性能有很大影响。如果你的UI在你的方法正在执行时暂停,那么考虑在另一个线程上运行它(用AsyncTask)

要减少垃圾收集,您需要减少循环中分配的内存量

我建议