美文网首页
如何定位内存抖动?StringBuilder.toString(

如何定位内存抖动?StringBuilder.toString(

作者: SMSM | 来源:发表于2018-04-03 20:31 被阅读245次

    优化方向

    内存问题

    • 泄漏
    • 大对象
    • 抖动

    页面加载速度

    冷热启动

    页面卡顿

    • UI刷新框架:事件驱动型,虽然便于管理。
    • 各个链路耗时优化。
    • GC带来暂停。
      • 对象池
      • UI复用

    定位大对象、定位内存泄漏点、定位抖动代码点(异步事件、同步事件造成的抖动,掺杂业务复杂度的抖动点最难排查)

    一、内存耗用情况分析

    1、我们硬件设备的易用性差,因为设备内存小,引发频繁的GC,每次GC占用至少50ms,这个过程会暂停APP进程,导致UI短暂无响应。紧接着如何定位引起抖动的代码位置呢?好难啊!结合Monitor我们能知道点菜大流程存在抖动,但是流程是个复杂的、异步流程,人肉看代码分析,是不可能的,不同于之前代码量少、存在For循环频繁创建对象等场景。

    2、如何定位抖动代码位置呢?我们先从定位 抖动对象开始,也就是过程中产生的对象,哪些是有效对象?哪些是无效对象?我们再看是否存在大量耗用内存的无效对象。然后再从对象找到调用链,定位代码位置。

    3、如何定位抖动对象呢?我们已知的能够通过record alloc获取一段时间内总共分配了哪些对象、分配的多少次,这是个差量数据!能够通过dumpheap hprof获取当前时间点的内存快照。通过alloc是看不出哪些是临时对象!通过hprof可悲的是内存快照的现在式,抖动是过去式。如果alloc 和 hprof 配合呢? 分析思路又是什么样的呢?

    4、不卖官司了,思路如下图。采集两次hprof 和 1次alloc,alloc在两次hprof中间,并且alloc过程要尽可能多的触发抖动,目的是为了扩大特征点,减少干扰。对alloc、hprof进行top排序,两次hprof每一项的的差值应该等于alloc中对应的该项,若存在很大偏差,说明该项存在大量临时对象,为GC扫描加重了负担。

    屏幕快照 2018-04-03 下午1.29.58.png 屏幕快照 2018-04-03 下午12.57.57.png 屏幕快照 2018-04-03 下午12.58.02.png

    5、对比发现,char[] 耗用内存 11M,String耗用5M。均属于临时对象。下一步,我们看调用链路在哪个点产生的,找到点后,发现都经过了String.format() 和 StringBuilder.append() 。看源码后知道了StringBuilder内部经历了 String -> char[] -> String 2个转化过程,每次转化,都会申请一块新内存,导致产生至少2块同等大小的新内存,新内存都是临时使用的,也就是临时垃圾,会被GC掉的。

    屏幕快照 2018-04-03 下午8.27.19.png

    6、哎妈呀,终于可以收割了,为保险起见,我又做了三个对比实验,验证StringBuilder.toString()的过程是如何的耗用内存。

    7、实验代码如下:

    1) 屏幕快照 2018-04-03 下午5.11.25.png 2) 屏幕快照 2018-04-03 下午5.08.42.png 3) 屏幕快照 2018-04-03 下午5.51.20.png
    对比结果好可怕:
    第一个GC掉10K,第二个GC掉49K,第三个GC掉87K。第二个过程,比第一个多产生40kb。
    04-03 16:51:08.397 12602-12608/? D/dalvikvm: GC_EXPLICIT freed <1K, 18% free 3065K/3708K, paused 1ms+1ms, total 17ms 
    04-03 16:51:13.997 12602-12608/? D/dalvikvm: GC_EXPLICIT freed 10K, 18% free 3065K/3708K, paused 6ms+2ms, total 50ms 
    04-03 16:51:14.517 12602-12608/? D/dalvikvm: GC_EXPLICIT freed <1K, 18% free 3065K/3708K, paused 1ms+1ms, total 14ms 
    04-03 16:51:01.167 12602-12608/? D/dalvikvm: GC_EXPLICIT freed <1K, 18% free 3065K/3708K, paused 1ms+1ms, total 15ms 
    04-03 16:51:07.447 12602-12608/? D/dalvikvm: GC_EXPLICIT freed 49K, 18% free 3065K/3708K, paused 6ms+3ms, total 53ms 
    04-03 16:51:07.987 12602-12608/? D/dalvikvm: GC_EXPLICIT freed <1K, 18% free 3065K/3708K, paused 2ms+1ms, total 15ms 
    04-03 17:20:15.067: D/dalvikvm(23173): GC_EXPLICIT freed 87K, 15% free 3062K/3584K, paused 2ms+2ms, total 24ms 
    04-03 17:05:15.217: D/dalvikvm(19590): GC_EXPLICIT freed <1K, 15% free 3062K/3572K, paused 2ms+2ms, total 24ms
    
    04-03 16:57:46.707 17926-17926/? D/MainActivity:  builder length  19491  
    04-03 16:57:47.467 17926-17926/? D/MainActivity:  msg length  19480  
    04-03 16:57:48.027 17926-17926/? D/MainActivity:  builder length  19491
    
    https://blog.csdn.net/liyanjing1987/article/details/38317787
    
    StringBuilder 是否消耗内存
    
    public String toString() {
    
    if (count == 0) {
    
    return "";
    
    }
    
    return StringFactory.newStringFromChars(0, count, value);
    
    }
    

    8、我们代码里,存在大量StringBuilder 和 Format的过程,每个过程,耗用3倍临时内存,经过以下过程,要耗用10倍临时内存。每次GC引发至少50ms的暂停!我们UI响应慢的主要原因。

    CalculateManager.calculate
    
    ---》 orderCalculateParam.toString(); @1
    
    CalculateLogImpl LOGGER.info("@calculate - CalculateManager {} {}", new Object[]{paramLog, orderCalculateResult});
    
    ———》 msg = msg.replaceAll("\\{\\}", "%s");(可忽略不计算)
    
    ———》 msg = String.format(msg, objects); @1 @1 @1 String - subString - value[] - String
    
    ---》 MessageFormat.format(FORMAT_EXPRESSION, CashierDeskPrefix.M, content); @1 @1
    
    LogMonitor
    
    ———》 sb.append(date).append(" | ").append("[").append(DEVICE_TYPE).append("]").append(tag).append(" : ").append(info).append("\n"); @1
    
    ———》 sb.toString() @1
    
    ———》 bufferedSink.writeUtf8(str); @1
    
    

    9、解决办法呢?目标是,减少临时垃圾。我们的场景是每个操作到要日志序列化到本地,日志多了之后,对日志管理的过程存在大量格式化拼接,比如StringBuilder、Format,解决办法有两个思路 A\减少类型转化 B\char[]内存复用。

    10、char[] 内存复用,及时有应该如何使用呢?

    相关文章

      网友评论

          本文标题:如何定位内存抖动?StringBuilder.toString(

          本文链接:https://www.haomeiwen.com/subject/oalxhftx.html