StringBuffer使用不当,引起的性能问题
2017-11-22 15:43
302 查看
最近,因为公司让我完善下调试工具的一个功能,需要用到缓冲区进行数据暂存,原先的想法是缓冲区上限设为5M,当数据存满的时候,删除最先加入的数据,再加入新的数据。傻逼逼的我想到的是使用StringBuffer进行临时存储,直接调用了如下方法来设置缓冲区的上限:
然后直接开始往StringBuffer写数据,发现能够成功写入数据,并且执行完具体操作后,能正常写出到文件,就觉得完成任务了,让测试去测试该项目,(测试没注意测试上限),然后项目就正常上线了。
直到过了几天······,主管找到我,说了句,为什么拿到的文件大于5M,有些甚至到达了30多M?
一句话瞬间把我整懵逼了,马上找原因,分析问题所在。才知道原来StringBuff是并不能设置缓冲区上限的,只能自动通过写闭环的形式来让StringBuffer形成上限。
问题解决!!!
另一解决办法:使用循环队列
4000
来进行闭环存储(推荐使用,能够将缓存控制在20M以内):
注:如果不设置缓冲区上限的后果(Android开发):
1.如果进程为系统核心进程,不会被kill,会导致进程占用过多堆栈空间,且不会被系统自动kill来释放空间,导致整体系统速率被严重拖慢,可能会导致死机
2.如果进程为非系统核心进程,同样也会进程占用过多堆栈空间,但是系统会自动kill进程来释放堆栈内存,偶现系统运行卡顿后正常。
3.恶意增长的情况下会引发OOM(out of memory)问题,导致系统崩溃
开发者可以通过
adb shell getprop | grep heap 来获取当前系统的堆栈空间信息
adb shell dumpsys meminfo packageName来查看当前进程运行的堆栈空间。
* MEMINFO in pid 12359 [packageName] *
private static StringBuffer stringBuffer=new StringBuffer(5442280);
然后直接开始往StringBuffer写数据,发现能够成功写入数据,并且执行完具体操作后,能正常写出到文件,就觉得完成任务了,让测试去测试该项目,(测试没注意测试上限),然后项目就正常上线了。
直到过了几天······,主管找到我,说了句,为什么拿到的文件大于5M,有些甚至到达了30多M?
一句话瞬间把我整懵逼了,马上找原因,分析问题所在。才知道原来StringBuff是并不能设置缓冲区上限的,只能自动通过写闭环的形式来让StringBuffer形成上限。
//这里缓存使用总量约25M,若将缓存数据写入内存时,因为占用io线程,会导致缓冲区使用增值50~60M,所以要处理好数据的使用 StringBuffer stringTempBuffer=new StringBuffer(5442880); while ((line = reader.readLine()) != null) { stringTempBuffer().append(line + "\n"); if (stringTempBuffer().length()>5000000) { stringBuffer=stringTempBuffer; //stringTempBuffer.delete(0,400000); //达到5M上限,删除最初的400K数据 //后续补充,delete并不会重置数组长度,并不能在根源处彻底解决问题 //最好的做法是新建一个临时buffer,来保存 stringTempBuffer=new StringBuffer(5442880); //原先的stringTempBuffer会由于没有被引用而被GC回收,释放内存 } }
问题解决!!!
另一解决办法:使用循环队列
4000
来进行闭环存储(推荐使用,能够将缓存控制在20M以内):
/** * author: 李文烙 * date: 2017/11/29 * @Desc:循环队列工具类 */ public class CircleQueue{ /** * 循环队列 (数组)默认大小 */ private final int DEFAULT_SIZE = 1000; /** * (循环队列)数组的容量 */ public int capacity; /** * 数组:保存循环队列的元素 */ public char[] elementData; /** * 队头(先见先出) */ public int head = 0; /** * 队尾 */ public int tail = 0; /** * 以循环队列 默认大小创建空循环队列 */ public CircleQueue() { capacity = DEFAULT_SIZE; elementData =new char[capacity]; } /** * 以指定长度的数组来创建循环队列 * * @param initSize */ public CircleQueue(final int initSize) { capacity = initSize; elementData =new char[capacity]; } /** * 获取循环队列的大小(包含元素的个数) */ public int size() { if (isEmpty()) { return 0; } else if (isFull()) { return capacity; } else { return tail + 1; } } /** * 插入队尾一个元素 */ public void add(final char element) { if (isEmpty()) { elementData[0] = element; } else if (isFull()) { elementData[head] = element; head++; tail++; head = head == capacity ? 0 : head; tail = tail == capacity ? 0 : tail; } else { if(tail<capacity-1){ elementData[tail + 1] = element; }else{ head=0; tail=capacity-2; } tail++; } } public boolean isEmpty() { return tail == head && tail == 0 && (String.valueOf(elementData[tail])==null||String.valueOf(elementData[tail]).equals("")); } public boolean isFull() { return head != 0 && head - tail == 1 || head == 0 && tail == capacity - 1; } public void clear() { elementData=new char[capacity]; head = 0; tail = 0; } /** * @return 取 循环队列里的值(先进的index=0) */ public char[] getQueue() { final char[] elementDataSort =new char[capacity]; final char[] elementDataCopy = elementData.clone(); if (isEmpty()) { } else if (isFull()) { int indexMax = capacity; int indexSort = 0; for (int i = head; i < indexMax;) { if (indexSort<capacity&&i<capacity) { elementDataSort[indexSort] = elementDataCopy[i]; indexSort++; i++; } if (i == capacity) { i = 0; indexMax = head; } if (indexSort==capacity){ break; } } } else { // elementDataSort = elementDataCopy;//用这行代码代替下面的循环,在队列刚满时候会出错 for (int i = 0; i < tail; i++) { if (tail<=capacity-1) { elementDataSort[i] = elementDataCopy[i]; } } } return elementDataSort; } } 用法: DebugApplication mContext=DebugApplication.getInstance(); while ((line = reader.readLine()) != null) { //读取不为空的情况下,把log存储在临时变量 // mContext.getCahceBuffer().append(line + "\n"); for (char i:line.toCharArray()) mContext.getCircleQueue().add(i); mContext.getCircleQueue().add('\n'); }
注:如果不设置缓冲区上限的后果(Android开发):
1.如果进程为系统核心进程,不会被kill,会导致进程占用过多堆栈空间,且不会被系统自动kill来释放空间,导致整体系统速率被严重拖慢,可能会导致死机
2.如果进程为非系统核心进程,同样也会进程占用过多堆栈空间,但是系统会自动kill进程来释放堆栈内存,偶现系统运行卡顿后正常。
3.恶意增长的情况下会引发OOM(out of memory)问题,导致系统崩溃
开发者可以通过
adb shell getprop | grep heap 来获取当前系统的堆栈空间信息
[dalvik.vm.heapgrowthlimit]: [128m] //受控情况下的极限堆,超过会引起OOM [dalvik.vm.heapmaxfree]: [8m] [dalvik.vm.heapminfree]: [512k] [dalvik.vm.heapsize]: [384m] [dalvik.vm.heapstartsize]: [8m] [dalvik.vm.heaptargetutilization]: [0.75]
adb shell dumpsys meminfo packageName来查看当前进程运行的堆栈空间。
Applications Memory Usage (kB): Uptime: 13677750 Realtime: 13677750
* MEMINFO in pid 12359 [packageName] *
相关文章推荐
- 无比奇怪的问题,Runtime报错,程序仍可运行(有可能是线程崩溃,但主程序不崩溃,线程崩溃可能是因为锁使用不当引起的)
- 关联查询中使用TOP引起的性能问题分析
- 由Memcached使用不当而引发性能问题的两个经验总结
- 对JavaEE的十大不当使用导致的性能问题
- WPF小记——DockPanel使用不当引起界面显示问题
- beforePhase使用不当导致ADF应用性能问题
- 由Memcached使用不当而引发性能问题的两个经验总结
- pt-online-schema-change使用中的不当,引起的数据库不可写入问题
- Android性能优化(三)——Handler使用不当引起的内存泄露
- 使用DBMS_STATS重放性能问题
- 使用spotligh+sqltuning+loadrunner对数据库性能问题进行定位和分析
- Xbox摇身变NAS:BT的使用问题与性能测试
- 使用网上流传的一个数据库连接池在Proxy.newProxyInstance处引起 java.lang.ClassCastException 问题的解决方法
- 使用 WAS V6.1 缺省的自签署证书可能引起的问题
- 关于使用FOR UPDATE引起阻塞的问题
- Linq to Sql中Single写法不当可能引起的数据库查询性能低下
- Hibernate分页可能引起的性能问题
- [问题大概解决了,应该就是单键模式使用不当产生的。明天找人来做一下压力测试]又一次斗胆发到了首页。期盼帮我检查下objDB类是否有问题
- 如何正确使用事件探察器解决性能问题(http://www.cnblogs.com/tom-fu/archive/2008/06/25/1146934.html#1235440)