美文网首页
性能优化 - 内存优化一

性能优化 - 内存优化一

作者: 慎独静思 | 来源:发表于2022-04-28 07:34 被阅读0次

    内存问题主要涉及内存泄露,内存溢出,内存过高等,本篇内容主要包括内存检测,内存问题处理,以及开发过程中需要注意的防止内存泄露的知识点。

    从APP释放内存的唯一方法时释放APP持有的对象引用,使它能让GC回收。
    GC是一种内存管理的环境,它有两个目标:找到程序中未来不会使用的对象进行回收,并且回收这些对象使用的资源。
    Android内存分为Young generation,older generation和permanent generation。对象占据的内存大小对于每一种类型来说都是有限制的。一旦有一种类型的内存满了,系统会执行GC来释放内存。GC的周期取决于当前正在回收的是哪一种类型的内存和每一种内存中的活跃对象数。
    尽管GC的速度很快,它仍然会影响应用的性能。我们不能决定GC是否执行,系统会根据当前的条件决定是否执行GC,当条件满足时,系统会停止执行程序,并开始执行GC。如果GC发生在程序执行中,比如动画或音乐,它会延长处理时间,这可能会影响程序的刷新。
    另外,你的代码也可能会使垃圾回收更加频繁或时间更长。比如:你在for循环中分配了大量的对象就会导致垃圾回收更加频繁,从而降低应用性能。
    每个应用占用的内存是有上限的,一旦到达限制内容,还申请分配内存的话,会收到OutOfMemoryError。很多情况下,我们需要知道应用在系统中可占用的最大内存是多少,咱们可以调用ActivityManager#getMemoryClass获取当前值,单位是M。
    咱们都知道应用退到后台时,进程仍然存在,应用持有的很多资源没有被释放,即使用户现在不再使用它了,这可能会影响系统的整体性能。当系统运行在低内存状态时,会杀掉后台进程来释放内存,你的进程持有的资源越少,被杀掉的可能性更低。


    image.png

    Android主要有两种处理低内存的方式:
    kswapd(kernel swap daemon)是Linux内核的一部分,它被用来把使用过的内存转换为空闲内存。当设备的空闲内存低时,kswapd变为活跃。Linux内核有一个空闲内存最高和最低的阈值。当空闲内存低于最小值时,kswapd开始回收内存。一旦空闲内存到达最高值,kswapd停止回收内存。
    如果空闲内存低于一个特定的阈值,系统开始杀进程来释放内存。
    很多情况下kswapd不能给系统释放足够的内存。系统使用onTrimMemory方法通知APP,当前系统内存低需要释放资源。如果仍然无法满足内存需求,内核会杀进程来释放内存。
    LMK(low-memory killer)使用一个OOM分数来决定杀掉那个进程。分数越高的会被先杀掉。后台进程会被先杀掉,系统进程会最后杀掉。
    RSS (Resident Set Size):APP使用的分享内存和正常内存。
    PSS (Proportional Set Size):APP使用的内存和分享内存的平均数(比如:3个APP分享3MB,那么每一个APP在PSS的计算中有1MB)
    USS (Unique Set Size):APP使用的正常内存,不包含分享内存。
    RAM(Random-access memory)

    onTrimMemory

    Dalvik GC log

    GC 事件发生时,会有log输出。

    D/dalvikvm(PID): GC_Reason Amount_freed, Heap_stats, External_memory_stats, Pause_time
    
    D/dalvikvm( 9050): GC_CONCURRENT freed 2049K, 65% free 3571K/9991K, external 4703K/5261K, paused 2ms+2ms
    

    GC_Reason分为以下几种:
    GC_CONCURRENT:随着你使用的堆内存不断升高,一个用来释放堆内存的GC操作。
    GC_FOR_MALLOC:当你APP的堆内存快要用光时,APP尝试申请更多的堆内存触发这次GC,因此系统不得不停止你的应用,进行内存回收操作。
    GC_HPROF_DUMP_HEAP:当你请求创建HPROF文件来分析堆内存时触发的GC
    GC_EXPLICIT:当你调用gc()方法时触发的明确GC操作(咱们应该避免调用此方法,选择相信系统GC)
    GC_EXTERNAL_ALLOC:这个只发生在API 10及以下,是针对外部分配内存的GC。

    Amount freed:本次GC回收的内存数量。
    Heap stats:空闲堆内存的百分比和(已使用堆内存/堆内存总大小)
    External memory stats:在api 10 及以下外部已分配内存(已分配内存数量/内存大小限制)
    Pause time:越大的堆内存需要越长的回收时间。它显示两个暂停时间:回收开始时和回收结束时的暂停时间。

    如果Heap stats持续增长,那可能发生了内存泄露。

    Art GC Log

    不像Dalvik,art不记录非明确请求GC的log。GC操作只在慢的情况下被打印。具体来说就是,如果GC暂停超过5ms或者GC周期超过100ms。

    I/art: GC_Reason GC_Name Objects_freed(Size_freed) AllocSpace Objects,
        Large_objects_freed(Large_object_size_freed) Heap_stats LOS objects, Pause_time(s)
    
    I/art : Explicit concurrent mark sweep GC freed 104710(7MB) AllocSpace objects,
        21(416KB) LOS objects, 33% free, 25MB/38MB, paused 1.230ms total 67.216ms
    

    GC Reason:
    Concurrent:一个不会暂停APP线程的同步GC操作,它运行在后台线程并不会阻止分配。
    Alloc:当你的堆内存将近满的时候,你的APP仍然尝试分配内存会触发此种GC。在这种情况下,GC操作发生在分配线程。
    Explicit:APP明确请求的GC操作,通过调用gc()方法。咱们应该选择相信系统GC逻辑,避免手动触发GC,并且有时会导致卡顿。
    NativeAlloc:native内存分配比如:Bitmaps or RenderScript分配对象引起的native内存压力触发此种GC。
    CollectorTransition:这是指一个堆内存转换引起的内存回收。这是由运行时更换GC策略引起的。这个只发生在8.0之前的低内存设备。
    HomogeneousSpaceCompact,DisableMovingGc和HeapTrim。
    如果咱们在log中看到了大量的GClog,可以观察一下heap stats的增长,如果这个值不断增大,那你很可能有内存泄露。
    同样的,如果你看到"Alloc"原因的GC log,那么你的堆内存已经解决临界值了,可能马上会发生OOM

    dumpsys meminfo

    adb shell dumpsys meminfo package_name|pid [-d]
    

    咱们可以通过以上命令获取当前应用的内存状态。


    Screen Shot 2022-05-14 at 7.19.59 AM.png
    Private (Clean and Dirty) RAM

    这个是仅被你的进程使用的内存。这部分RAM是当你的进程销毁时可被系统回收的部分。一般而言,Private Dirty RAM是最重要的部分,因为它只能被你的进程使用,并且只能存在于RAM中。你的进程创建的全部Dalvik和native 堆内存都属于Private dirty ram。你的进程和Zygote进程分享的Dalvik和native内存属于share dirty ram。

    Proportional Set Size (PSS)

    这是你的应用的RAM使用的测量值,包括在进程直接分享的部分。属于你进程的RAM部分会被算进此值中,你的进程和其他进程分享的RAM,会取一个分享内存的平均值算进此值中。比如:3个APP分享3MB,那么每一个APP在PSS的计算中有1MB。
    因此PSS是一个应用实际内存的很好的测量值。

    一般而言我们只需要关心PSS Total和Private Dirty,在一些情况下,我们需要关心Private Clean 和 Heap Alloc。

    Dalvik Heap

    你的APP使用的Dalvik RAM,PSS Total包含全部的Zygote 分配内存。Private Dirty只是你的APP分配RAM。Heap Alloc是Dalvik和native堆内存分配的内存数量。这个值大于Private Total和Private Dirty,因为它包含你和其他进程分享的全部内存。

    Unknown

    不能被划分为以上分类的其他内存。一般来说,现在主要指不能被工具识别的native内存。

    TOTAL

    这是你的进程使用的总的Proportional Set Size (PSS),它代表你的进程使用的总的内存。

    ViewRootImpl

    你的进程中活跃的root view个数。每一个root view关联一个window,这能帮忙我们失败内存溢出是涉及Dialog还是window。

    AppContexts and Activities

    你的进程中APP context和Activity的个数,这可以帮你快速失败Activity个数,来判断是否有Activity泄露。

    请移步
    性能优化 - 内存优化二

    参考:
    1、https://developer.android.com/topic/performance/memory-overview
    2、https://developer.android.com/studio/profile/memory-profiler
    3、https://developer.android.com/studio/debug/am-logcat#memory-logs
    4、https://developer.android.com/studio/command-line/dumpsys#meminfo

    相关文章

      网友评论

          本文标题:性能优化 - 内存优化一

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