Java 高效循环,避免Android的多个垃圾收集调用

Java 高效循环,避免Android的多个垃圾收集调用,java,android,for-loop,garbage-collection,Java,Android,For Loop,Garbage Collection,我有一个Android应用程序,允许用户记录数据(如加速计、纬度、经度等)。这些字段总共有9个,用户最多可以录制10分钟(每个字段3000条记录)。因此,总共可以收集27000个数据点。用户还可以通过拍照和视频上传SD卡 当用户收集完数据(或10分钟结束)后,数据存储在字符串中,随后作为.csv文件上传到SD卡。但问题是,由于大量的垃圾收集(看起来大约每秒5次收集左右!),数据附加到字符串需要花费很长时间,因此附加开始得很快,但随着数据的添加越来越多,附加速度似乎也越来越慢 这是导致滞后的循环:

我有一个Android应用程序,允许用户记录数据(如加速计、纬度、经度等)。这些字段总共有9个,用户最多可以录制10分钟(每个字段3000条记录)。因此,总共可以收集27000个数据点。用户还可以通过拍照和视频上传SD卡

当用户收集完数据(或10分钟结束)后,数据存储在字符串中,随后作为.csv文件上传到SD卡。但问题是,由于大量的垃圾收集(看起来大约每秒5次收集左右!),数据附加到字符串需要花费很长时间,因此附加开始得很快,但随着数据的添加越来越多,附加速度似乎也越来越慢

这是导致滞后的循环:

           for( i = 0 ; i < len2 ; i++ ) {
                data += accelX[i] + ", " + accelY[i] + ", " + accelZ[i] + 
                ", " + accelT[i] + ", " + latitu[i] + ", " + 
                longit[i] + ", " + orient[i] + ", " + 
                magneX[i] + ", " + magneY[i] + ", " + magneZ[i] + 
                ", " + millis[i] + "\n";

                partialProg = 100.0 * ( (double)(i+1) / (double)(len2));
                dia.setProgress((int) partialProg);

            }
(i=0;i{ 数据+=accelX[i]+”,“+accelY[i]+”,“+accelZ[i]+ “,“+accelT[i]+”,“+Lattu[i]+”,“+ 长[i]+“,”方向[i]+“,” magneX[i]+”,“+magneY[i]+”,“+magneZ[i]+ “,”+millis[i]+“\n”; partialProg=100.0*((双精度)(i+1)/(双精度)(len2)); 直径设置进度((内部)部分进度); }
data
只是一个字符串,没有任何内容被
new
ed,所以我不确定为什么GC被频繁调用。我的问题是:这里的问题是什么,和/或如何提高效率?

您正在创建许多对象。每次使用
操作符+
,实际上都会创建一个新对象,它是可扩展的[如果重复多次]

您可以通过使用and来提高效率,并在完成时创建字符串

例如:

sb.append(accelX[i]).append(',').append(accelY[i]).append(',').append(accelZ[i]);
StringBuffer buff = new StringBuffer();
buff.append(accelX[i]).append(...).apend(...)

[其中sb是StringBuilder的实例]

您可以做的一个改进是,使用StringBuffer来构造数据,这样您就可以避免字符串构造和连接操作。例如:

sb.append(accelX[i]).append(',').append(accelY[i]).append(',').append(accelZ[i]);
StringBuffer buff = new StringBuffer();
buff.append(accelX[i]).append(...).apend(...)

您可以使用StringBuilder连接数据:

StringBuilder sb = new StringBuilder();
for( i = 0 ; i < len2 ; i++ ) {
    sb.append(accelX[i]).append(", ");
    sb.append(accelY[i]).append(", ");
    sb.append(accelZ[i]).append(", ");
    sb.append(accelT[i]).append(", ");
    sb.append(latitu[i]).append(", ");
    sb.append(longit[i]).append(", ");
    sb.append(orient[i]).append(", ");
    sb.append(magneX[i]).append(", ");
    sb.append(magneY[i]).append(", ");
    sb.append(magneZ[i]).append(", ");
    sb.append(millis[i]).append("\n");
}
StringBuilder sb=新建StringBuilder();
对于(i=0;i
StringBuilder用于构建长字符串

它还避免了将如此多的字符串对象分配到堆中;因为每个+=操作符都在创建一个新的字符串对象,而不是修改最后一个。这反过来会导致大量GC调用来清理所有redundent字符串对象。另见:


正如马塞洛指出的那样;您还可能发现在内存中处理大量数据可能会在低规格Android设备上出现问题,此时您应该考虑将StrigBu建器的内容附加到每个X次迭代次数的临时文件中,以保持占用率较低。在流程结束时,您可以将文件流式传输到计划的目标位置,或根据需要将其段读回内存。

+
字符串上的运算符实际使用的是
StringBuilder
,因此每个循环至少执行两次分配(新建
StringBuilder
,然后
StringBuilder
在调用
.toString()
将结果分配给
数据时创建一个字符串)。
解决这个问题的最佳方法是在循环前面创建一个
StringBuilder

StringBuilder buf = new StringBuilder();
String sep = ", ";
for( i = 0 ; i < len2 ; i++ ) {
    buf.append(accelX[i]).append(sep).append(accelY[i]).append(sep);
    buf.append(accelZ[i]).append(sep).append(accelT[i]).append(sep);
    buf.append(latitu[i]).append(sep).append(longit[i]).append(sep);
    buf.append(magneX[i]).append(sep).append(magneY[i]).append(sep);
    buf.append(magneZ[i]).append(sep).append(millis[i]).append('\n');
    partialProg = 100.0 * ( (double)(i+1) / (double)(len2));
    dia.setProgress((int) partialProg);
}
StringBuilder buf=new StringBuilder();
字符串sep=“,”;
对于(i=0;i
为什么不使用某种类型的数据存储结构(例如,最合理的数据库)?一旦将每一轮数据存储在数据库中,它就基本上被“遗忘”了在编译CSV之前,请考虑使用一个打印机和一个BuffReDeDrices组合并直接写入文件。如果您仍然希望在内存中拥有字符串,我建议使用String Script。- 1,仍然在附件中创建很多字符串对象:<代码> AcxL[i] +,+..…
也在创建更多的字符串对象并非每个+运算符都创建了一个新的字符串对象,您发布的链接就可以揭穿这一点。一个带有+运算符的表达式使用了一个StringBuilder。所有其他答案都很有帮助,但由于很好的解释/完整的代码示例,批准了这个答案。谢谢,这很有效。U Mad:他不是在执行一个表达式,而是在每个循环上附加“数据”。但你是对的,在单赋值情况下,编译器可以使用StringBuilder优化运算符。