内存分析_内存分析工具 - CSDN
精华内容
参与话题
  • 性能优化——内存分析工具的使用

    千次阅读 2018-05-30 08:10:47
    本文将介绍比较常用的的内存泄漏检测工具,包括HeapSnapShot、HeapViewer、MAT一、HeapSnapShot的使用HeapSnapShot意思是堆快照,通过堆内存的信息来分析内存泄漏的问题。1、启动HeapSnapShot2、显示Heap SnapShot...
    本文将介绍比较常用的的内存泄漏检测工具,包括HeapSnapShot、HeapViewer、MAT
    一、HeapSnapShot的使用
    HeapSnapShot意思是堆快照,通过堆内存的信息来分析内存泄漏的问题。
    1、启动HeapSnapShot
    2、显示Heap SnapShot面板
    点击截图部分的左上角,可以看到有个App heap,点开,里面还有Image heap、Zygote heap,分别代码app堆内存信息、图片堆内存信息、zygote进程的堆内存信息。
    A区域:
    列举了堆内存中的所有的类,以下是列表中列名:
    名称意义
    Total Count内存中该类的对象个数
    Heap Count堆内存中该类的对象个数
    Sizeof物理大小
    Shallow size该对象本身占有内存大小
    Retained Size释放该对象后,节省的内存大小
    B区域:
    当我们点击某个类时,右边就会显示该类的实例化对象,这里会显示多少个实体,以及详细信息。
    名称意义
    depth深度
    Shallow Size对象本身内存大小
    Dominating Size管辖的内存大小
    当你点击某个对象时,将展开对象内部有哪些对象,同时C区域也会显示哪些对象引用了该对象:
    C区域:
    某对象引用树对象,在这里可以看到其被谁引用了,比如内存泄漏中,可以看出它被谁引用
    二、Heap Viewer工具使用
    heap viewer能做什么
    实时查看app分配的内存大小和空间内存大小
    发现memory leaks
    heap viewer的使用条件
    5.0以上的系统,包括5.0
    开发者选项可以用
    Heap Viewer启动:
    Heap Viewer面板:
    按上图的标记顺序按下,我们就能看到内存的具体数据,右边面板中数值在每次GC时发生改变,包括app自动触发或者你你来手动触发
    面板中的名词:

    Heap Size堆栈分配给App的内存大小
    Allocated已分配使用的内存大小
    Free空闲的内存大小
    %UsedAllocated/Heap Size,使用率
    Objects对象数量


    类型意义
    free空闲的对象
    data object数据对象,类类型对象,最主要的观察对象
    class object类类型的引用对象
    1-byte array(byte[],boolean[])一个字节的数组对象
    2-byte array(short[],char[])两个字节的数组对象
    4-byte array(long[],double[])4个字节的数组对象
    non-Java object非Java对象
    下面是每一个对象都有的列名含义:
    列名意义
    Count数量
    Total Size总共占用的内存大小
    Smallest将对象占用内存的大小从小往大排,排在第一个的对象占用内存大小
    Largest将对象占用内存的大小从小往大排,排在最后一个的对象占用的内存大小
    Median将对象占用内存的大小从小往大排,拍在中间的对象占用的内存大小
    Average平均值
    当我们点击某一行时,可以看到如下的柱状图:

     
    横坐标是对象的内存大小,这些值随着不同对象是不同的,纵坐标是在某个内存大小上的对象的数量
    HeapViewer的使用
    那么如何使用Heap Viewer发现内存泄漏呢
    HeapViewer中的数值会自动在每次发生GC时会自动更新,那么我们是等着它GC吗?既然我们是来看内存泄漏,那么我们在需要检测内存泄漏的用例执行过后,手动GC下,然后观察data object一栏的total size(也可以观察Heap Size/Allocated内存的情况),看看内存是不是回到一个稳定值,多次操作后,只要内存是稳定在某个值,那么说明诶有内存泄漏,如果都在增长,不管快慢,都是存在内存泄漏的可能性。
    Heap Viewer不光可以用来检测是否有内存泄漏,对于内存抖动,我们也可以用该工具检测,因为内存抖动的时候,会频繁发生GC,这个时候我们只需要开启Heap Viewer,观察数据的变化,如果发生内存抖动,会观察到数据在段时间内频繁更新
    三、MAT的使用
    MAT工具全称为Memory Analyzer Tool,一款详细分析Java堆内存的工具,该工具非常强大,为了使用该工具,我们需要hprof文件,但是该文件不能直接被MAT使用,需要进行进一步转化,可以使用hprof-conv命令来转化,但是Android Studio可以直接转化,转化方法如下:
    1.选择一个hprof文件,点击右键选择Export to standard .hprof选项。 

     
    2.填写更改后的文件名和路径

    点击OK按钮后,MAT工具所需的文件就生成了,下面我们用MAT来打开该工具
    1、打开MAT后选择File->Open File选择我们刚生成的hprof文件
    2、选择该文件后,MAT会在几秒钟的时间解析该文件,有的hprof文件可能过大,会有更长时间解析,解析后,展现在我们面前的界面如下:
    这个是总览界面,会大体给出一些分析后初步的结论
    OverView视图:
    该视图会在首页总结出当前这个Heap Dump占用了多大的内存,其中涉及的类有多少,对象有多少,类加载器,如果没有回收的对象,会有一个连续,可以直接参看(图中的Objects Histogram)
    比如该例子中显示了Heap dump占用了41M的内存,5400个类,96700个对象,6个类加载器,然后还有各种分类信息
    Biggest Objects By Retained Size
    会列举出Retained Size的最大的几个值,你可以将鼠标直接放到饼图中的扇叶上,可以在右侧看出详细信息

    图中灰色区域,并不是我们需要关心的,他是除了大内存对象外的其他对象,我们需要关心的就是图中彩色区域,比如图中2.4M的对象,我们来看看该对象到底是啥:

     
    该对象是一个Bitmap对象,你如果想知道该对象到底是什么图片,可以使用图片工具gimp工具浏览该对象.
    histogram视图:
    histogram视图主要是查看某个类的实例个数,比如我们在检查内存泄漏时候,要判断是否频繁创建了对象,就可以来看对象的个数来看。也可以通过排序看出占用内存大的对象

    默认是类名形式展示,你也可以选择不同的显示方式,有以下四种方式

    下面来演示一下

    Dominator tree视图:

    该视图会以占用总内存的百分比来列举所有实例对象,注意这个地方是对象而不是类了,这个视图是用来发现大内存对象的。这些对象都可以展开查看更详细的信息,可以看到该对象内部包含的对象: 

    Leaks suspects视图:



    这个视图会展示一些可能的内存泄漏的点,比如上图上图显示有3个内存泄漏可疑点,我们以Problem Suspect 1为例来理解该报告,首先我们来看该可疑点详细信息

    上面信息显示ImageCahe类的一个实例0xa50819f8占用了14.19%的内存,具体值为5147200字节(5147200/1024/1024=4.9M),并存放在LinkedHashMap这个集合中,然后我们点击Details跳转到更详细的页面

     
    这样我们就能找到在我们的app源码中造成该泄漏可疑点的地方,很容易去定位问题.

     
    展开全文
  • Linux常见的内存分析工具

    千次阅读 2019-02-17 15:08:00
    随着技术的日新月异,嵌入式软件产品也逐渐往更高端, ...本文将列出几个我比较熟悉的内存分析的工具或者方法,读者不妨可以尝试使用以下。 1. system monitor图像化界面 在ubuntu左上角点击search your comput...

        随着技术的日新月异,嵌入式软件产品也逐渐往更高端, 运行更流畅方向发展。正常我们目前的手机产品,常见的基本都是32G,16G,已经很难在看到1G的内存了。即使如此我们还是会面临内存不够用导致系统慢的结果。本文将列出几个我比较熟悉的内存分析的工具或者方法,读者不妨可以尝试使用以下。

    1. system monitor图像化界面

    在ubuntu左上角点击search your computerà输入System MonitoràResources,便可以看到目前内存的使用情况。       

    2. free

    free命令一般显示的是系统使用和空闲的内存.可以使用free -help查看使用方法

    从上图可以看到主要包含Mem和Swap,其中total表示全部的内存大小,used表示已经使用的内存大小, free表示剩下可以使用的内存大小,shared表示共享内存大小,一般用于进程间通信的, buffer是用于为块设备做缓存的,cache一般是指page cache用户缓存打开的文件的(可以提高文件的访问速度). 当系统内存短缺时会进行内存回收,其回收的部分主要在buffer/cache,但是这部分也不是全部可以回收的,比如共享的内存段,ram fs是不可以回收的,所以availabe 约等于free +buffer/cache.

    3. top

    top命令基本的信息描述在下图中已标出,其中用红色框标出的这里重点说明一下.S表示的是进程的状态, S表示Sleeping, R表示Running, D表示不可中断的睡眠状态,T表示trace跟踪状态,Z表示Zombie状态. CPU和MEM表示CPU和MEM的占用比.

    处理之外还有一些交互的命令,输入M将重排MEM按照从大到小,输入P重排CPU从大到小,输入T按照进程运行的时间排序,输入m显示内存的柱状图,输入F/f可以配置输出信息.

    4 vmstat

    具体的信息可以参考下图中的说明,但是如果要循环看到如下的信息可以使用比如vmstat 2 10,表示2s抓取一次共抓取10次.

     

    5. cat /proc/slabinfo 或者slabtop

    就下图而言,Ojects包含于slabs,slabs包含于caches,所以从归属关系来说Caches是最大的。从图中可以得到目前系统的slab所消耗的内存大概为153900K,在系统内存吃掉非常多的情况下我们便可以从其中查看是否有异常。

    6. pmap

    细心的朋友可能会发现,上述的种种工具都是从系统的角度出发的,那么如果想查看某一个进程所吃掉的内存,该用什么工具?这时候你就可以考虑使用pmap了,使用方法为:

    pmap PID

    下面我用一个简单的测试程序来示例:

    当然除此之外还可以加上一些参数可以显示更加详细的信息,可以使用pmap -x PID or pmap -X PID

    7.  dmesg

    默认情况下dmesg显示的log的时间戳是开机的时间,有的时候定位问题需要找到出问题时的点,着就会给研发工程师们带来一定的困惑,这里提供几个小技巧,或许会有帮助.

    可以看到,dmesg -T显示的时间戳为系统时间,dmesg -Td显示系统时间并且会给出两天相邻log的差值.

    下图表示的是系统的具体内存系统,有一个node,4个zone以及地址空间范围.其中包含2097005个page= 2097005 * 4KB = 8GB,正式测试电脑的内存大小.

    其中的memap是用于存放struct page的,在后续的文章中会详细介绍.

    8. cat /proc/meminfo

    meminfo中包含了内存相关的很多细节,比如匿名页面的大小,page cache的大小,所谓匿名页面指物理内存没有和实际的文件关联(malloc分配的内存就是匿名页面),page cache值分配的物理内存和时间的文件关联(比如用记事本打开一个文本文档,内存中包含文本文档中的内容).

    展开全文
  • 前文讲到了内存泄漏的原因,那么要怎么定位内存泄漏呢?这里列出了常用的分析工具及其使用方法 ...获取Java堆内存详细信息,可以分析内存泄漏的问题 在2.X版本中,Android Studio使用的分析工具 点击Mo...

    前文讲到了内存泄漏的原因,那么要怎么定位内存泄漏呢?这里列出了常用的分析工具及其使用方法
    以下Heap SnapshotMATHeap ViewerAllaction TrackingLeakCanaryTraceView资料均来源于网络

    Heap Snapshot

    获取Java堆内存详细信息,可以分析出内存泄漏的问题
    在2.X版本中,Android Studio使用的分析工具
    点击Monitor便可查看CPUMemoryNetworkGPU的情况
    Heap Dump启动
    其打开面板如下:
    Heap Snapshot控制面板
    该面板里的信息可以有三种类型:app heap/image heap/zygote heap
    分别代表app堆内存信息,图片堆内存信息,zygote进程的堆内存信息
    Heap Snapshot控制面板详细信息

    A区域

    列举了堆内存中所有的类,一下是列表中列名:

    名称 意义
    Total Count 内存中该类的对象个数
    Heap Count 堆内存中该类的对象个数
    Sizeof 物理大小
    Shallow size 该对象本身占有内存大小
    Retained Size 释放该对象后,节省的内存大小

    B区域

    当我们点击某个类时,右边的B区域会显示该类的实例化对象,这里面会显示有多少个实体,以及详细信息
    Heap Snapshot B区域

    名称 意义
    depth 深度
    Shallow Size 对象本身内存大小
    Dominating Size 管辖的内存大小

    当你点击某个对象时,将展开该对象内部含有哪些对象,同时C区域也会显示哪些对象引用了该对象

    C区域

    Heap Snapshot C区域
    点击查看
    Heap Snapshot C展开区域
    某对象引用树对象,在这里面能看出其没谁引用了,比如在内存泄漏中,可以看出来它被谁引用,比如上图,引用树的第一行,可以看出来,该对象被Object[12]对象引用,索引值为1,那我们展开后,可以看到,该Object[12]是一个ArrayList

    在3.X版本,Android Studio采用了新的分析工具,但其使用都是类似的
    其启动界面如下
    Android Profiler
    分析界面如下
    Android Profiler具体分析界面

    MAT

    下载:http://eclipse.org/mat/downloads.php
    MAT工具全称为Memory Analyzer Tool,一款详细分析Java堆内存的工具,该工具非常强大,为了使用该工具,我们需要hprof文件。但是该文件不能直接被MAT使用,需要进行一步转化,可以使用hprof-conv命令来转化,但是Android Studio可以直接转化,转化方法如下
    选择一个hprof文件,点击右键选择Export to standard .hprof选项
    MAT Android转化1
    MAT工具所需的文件就生成了,下面我们用MAT来打开该工具:

    1. 打开MAT后选择File -> Open File选择我们刚才生成的hprof文件
    2. 选择该文件后,MAT会有几秒种的时间解析该文件,有的hprof文件可能过大,会有更长的时间解析,解析后,展现在我们的面前的界面如下
      MAT 界面
      这是个总览界面,会大体给出一些分析后初步的结论
    • Overview视图
      该视图会首页总结出当前这个Heap dump占用了多大的内存,其中涉及的类有多少,对象有多少,类加载器,如果有没有回收的对象,会有一个连接,可以直接参看(图中的Unreachable Objects Histogram)。
      比如该例子中显示了Heap dump占用了41M的内存,5400个类,96700个对象,6个类加载器。
      然后还会有各种分类信息:
      • Biggest Objects by Retained Size
        会列举出Retained Size值最大的几个值,你可以将鼠标放到饼图中的扇叶上,可以在右侧看出详细信息,在这里可以找到我们关心的内容
      • histogram视图
        histogram视图主要是查看某个类的实例个数,比如我们在检查内存泄漏时候,要判断是否频繁创建了对象,就可以来看对象的个数来看。也可以通过排序看出占用内存大的对象,默认是类名形式展示,也可以选择不同的显示方式
      • Dominator tree视图
        该视图会以占用总内存的百分比来列举所有实例对象,注意这个地方是对象而不是类了,这个视图是用来发现大内存对象的。这些对象都可以展开查看更详细的信息,可以看到该对象内部包含的对象
      • Leaks suspects视图
        这个视图会展示一些可能的内存泄漏的点

    Navigation History中可以选择Histogram,然后右键加入对比,实现多个histogram数据的对比结果,从而分析内存泄漏的可能性

    Heap Viewer

    实时查看App分配的内存大小和空闲内存大小
    发现Memory Leaks

    • 使用条件
      5.0以上的系统,包括5.0
      开发者选项可用

    在2.x的Android Studio中,
    可以直接在Android studio工具栏中直接点击小机器人启动
    还可以在Android studio的菜单栏中Tools
    或者是在sdk的tools工具下打开
    在3.x的IDE中,默认已经找不到启动图标,但在tools目录下依旧可以打开使用

    Heap Viewer面板如下
    Heap Viewer面板
    按上图的标记顺序按下,我们就能看到内存的具体数据,右边面板中数值会在每次GC时发生改变,包括App自动触发或者你来手动触发
    总览:
    Heap Viewer面板总览

    列名 意义
    Heap Size 堆栈分配给App的内存大小
    Allocated 已分配使用的内存大小
    Free 空闲的内存大小
    %Used Allocated/Heap Size,使用率
    Objects 对象数量

    详情:
    Heap Viewer详情

    类型 意义
    free 空闲的对象
    data object 数据对象,类类型对象,最主要的观察对象
    class object 类类型的引用对象
    1-byte array(byte[],boolean[]) 一个字节的数组对象
    2-byte array(short[],char[]) 两个字节的数组对象
    4-byte array(long[],double[]) 4个字节的数组对象
    non-Java object 非Java对象

    下面是每一个对象都有的列名含义

    列名 意义
    Count 数量
    Total Size 总共占用的内存大小
    Smallest 将对象占用内存的大小从小往大排,排在第一个的对象占用内存大小
    Largest 将对象占用内存的大小从小往大排,排在最后一个的对象占用的内存大小
    Median 将对象占用内存的大小从小往大排,拍在中间的对象占用的内存大小
    Average 平均值

    当我们点击某一行时,可以看到如下的柱状图
    Heap Viewer柱状图
    横坐标是对象的内存大小,这些值随着不同对象是不同的,纵坐标是在某个内存大小上的对象的数量

    使用:在需要检测内存泄漏的用例执行过后,手动GC下,然后观察data object一栏的total size(也可以观察Heap Size/Allocated内存的情况),看看内存是不是会回到一个稳定值,多次操作后,只要内存是稳定在某个值,那么说明没有内存溢出的,如果发现内存在每次GC后,都在增长,不管是慢增长还是快速增长,都说明有内存泄漏的可能性

    Allaction Tracking

    追踪内存分配信息。可以很直观地看到某个操作的内存是如何进行一步一步地分配的
    Allocation Tracker(AS)工具比Allocation Tracker(Eclipse)工具强大的地方是更炫酷,更清晰,但是能做的事情都是一样的

    Allocation Tracker启动
    Allocation Tracker启动
    在内存图中点击途中标红的部分,启动追踪,再次点击就是停止追踪,随后自动生成一个alloc结尾的文件,这个文件就记录了这次追踪到的所有数据,然后会在右上角打开一个数据面板
    Allocation Tracker数据面板
    面板左上角是所有历史数据文件列表,后面是详细信息,好,现在我们来看详细介绍信息面板
    Allocation Tracker详细介绍信息面板
    下面我们用字母来分段介绍

    • A:查看方式选项
      A标识的是一个选择框,有2个选项
      Allocation TrackerA标识
      Group by Method:用方法来分类我们的内存分配
      Group by Allocator:用内存分配器来分类我们的内存分配
      不同的选项,在D区显示的信息会不同,默认会以Group by Method来组织,我们来看看详细信息:
      Allocation TrackerA标识信息
      从上图可以看出,首先以线程对象分类,默认以分配顺序来排序,当然你可以更改,只需在Size上点击一下就会倒序,如果以Count排序也是一样,Size就是内存大小,Count就是分配了多少次内存,点击一下线程就会查看每个线程里所有分配内存的方法,并且可以一步一步迭代到最底部
      Allocation TrackerA标识信息底部
      当你以Group by Allocator来查看内存分配的情况时,详细信息区域就会变成如下
      Allocation TrackerA标识信息底部详细信息
      默认还是以内存分配顺序来排序,但是是以每个分配者第一次分配内存的顺序
      Allocation TrackerA标识信息内存分配顺序
      这种方式显示的好处,是我们很好的定位我们自己的代码的分析信息,比如上图中,以包名来找到我们的程序,在这次追踪中包民根目录一共有五个类作为分配器分配了78-4-1=73次内存
    • B:Jump To Source按钮
      如果我们想看内存分配的实际在源码中发生的地方,可以选择需要跳转的对象,点击该按钮就能发现我们的源码,但是前提是你有源码
      Allocation TrackerB标识
      如果你能跳转到源码,Jump To Source按钮才是可用的,都是跳转到类
    • C:统计图标按钮
      该按钮比较酷炫,如果点击该按钮,会弹出一个新窗口,里面是一个酷炫的统计图标,有柱状图和轮胎图两种图形可供选择,默认是轮胎图,其中分配比例可以选择分配次数和占用内存大小,默认是大小Size
    • 轮胎图
      Allocation TrackerC标识轮胎图
      轮胎图是以圆心为起点,最外层是其内存实际分配的对象,每一个同心圆可能被分割成多个部分,代表了其不同的子孙,每一个同心圆代表他的一个后代,每个分割的部分代表了某一带人有多人,你双击某个同心圆中某个分割的部分,会变成以你点击的那一代为圆心再向外展开。如果想回到原始状态,双击圆心就可以了。
      1.起点
      Allocation TrackerC标识轮胎图起点
      圆心是我们的起点处,如果你把鼠标放到我图中标注的区域,会在右边显示当前指示的是什么线程(Thread1)以及具体信息(分配了8821次,分配了564.18k的内存),但是红框标注的区域并不代表Thread1,而是第一个同心圆中占比最大的那个线程,所以我们现在把鼠标放到第一个同心圆上,可以看出来,我们划过同心圆的轨迹时可以看到右边的树枝变化了好几个值
      Allocation TrackerC标识轮胎图起点信息
      2.查看某一个扇面
      我们刚打开是全局信息,我们如果想看其中某个线程,详细信息,可以顺着某个扇面向外围滑动,当然如果你觉得不还是不清晰,可以双击该扇面全面展现该扇面的信息
      Allocation TrackerC标识轮胎图查看信息
      在某个地方双击时,新的轮胎图是以双击点为圆心,你如果想到刚才的圆,双击圆心空白处就可以
      Allocation TrackerC标识轮胎图双击查看
      3.一个内存的完整路径
      Allocation TrackerC标识轮胎图内存完整路径
    • 柱状图
      Allocation TrackerC标识柱状图
      柱状图以左边为起始点,从左到右的顺序是某个的堆栈信息顺序,纵坐标上的宽度是以其Count/Size的大小决定的。柱状图的内容其实和轮胎图没什么特别的地方
      1.起点
      Allocation TrackerC标识柱状图
      2.查看某一个分支
      Allocation TrackerC标识柱状图查看分支
      3.Count/Size切换
      Allocation TrackerC标识柱状图切换

    LeakCanary

    可以直接在手机端查看内存泄露的工具
    实现原理:本质上还是用命令控制生成hprof文件分析检查内存泄露

    添加LeakCanary依赖包

    https://github.com/square/leakcanary
    在主模块app下的build.gradle下添加如下依赖

    debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1'
    releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1'
    

    LeakCanary添加依赖

    开启LeakCanary

    添加Application子类
    首先创建一个ExampleApplication,该类继承于Application,在该类的onCreate方法中添加如下代码开启LeakCanary监控:
    LeakCanary.install(this);
    LeakCanary添加Application子类

    在配置文件中注册ExampleApplication

    AndroidManifest.xml中的application标签中添加如下信息:
    android:name=".ExampleApplication"
    LeakCanary注册ExampleApplication

    这个时候安装应用到手机,会自动安装一个Leaks应用,如下图
    LeakCanary安装信息

    制造一个内存泄漏的点

    建立一个ActivityManager类,单例模式,里面有一个数组用来保存Activity:

    package com.example.android.sunshine.app;
    import android.app.Activity;
    import android.util.SparseArray;
    import android.view.animation.AccelerateInterpolator;
    import java.util.List;
    public class ActivityManager {
        private SparseArray<Activity> container = new SparseArray<Activity>();
        private int key = 0;
        private static ActivityManager mInstance;
        private ActivityManager(){}
        public static ActivityManager getInstance(){
            if(mInstance == null){
                mInstance = new ActivityManager();
            }
            return mInstance;
        }
    
        public void addActivity(Activity activity){
            container.put(key++,activity);
        }
    }
    

    然后在DetailActivity中的onCreate方法中将当前activity添加到ActivityManager的数组中:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_detail);
        ActivityManager.getInstance().addActivity(this);
        if (savedInstanceState == null) {
            // Create the detail fragment and add it to the activity
            // using a fragment transaction.
    
            Bundle arguments = new Bundle();
            arguments.putParcelable(DetailFragment.DETAIL_URI, getIntent().getData());
    
            DetailFragment fragment = new DetailFragment();
            fragment.setArguments(arguments);
    
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.weather_detail_container, fragment)
                    .commit();
        }
    }
    

    我们从首页跳转到详情页的时候会进入DetailActivityonCreate的方法,然后就将当前activity添加到了数组中,当返回时,我们没把他从数组中删除。再次进入的时候,会创建新的activity并添加到数组中,但是之前的activity仍然被引用,无法释放,但是这个activity不会再被使用,这个时候就造成了内存泄漏。我们来看看LeakCanary是如何报出这个问题的

    演示

    LeakCanary演示
    解析的过程有点耗时,所以需要等待一会才会在Leaks应用中,当我们点开某一个信息时,会看到详细的泄漏信息
    LeakCanary演示1

    TraceView

    从代码层面分析性能问题,针对每个方法来分析,比如当我们发现我们的应用出现卡顿的时候,我们可以来分析出现卡顿时在方法的调用上有没有很耗时的操作,关注以下两个问题:

    • 调用次数不多,但是每一次执行都很耗时
    • 方法耗时不大,但是调用次数太多
      简单一点来说就是我们能找到频繁被调用的方法,也能找到执行非常耗时的方法,前者可能会造成cpu频繁调用,手机发烫的问题,后者就是卡顿的问题

    TraceView工具启动

    打开Monitor,点击图中的标注的按钮,启动追踪
    TraceView工具启动

    TraceView工具面板

    打开App操作你的应用后,再次点击的话就停止追踪并且自动打开traceview分析面板
    TraceView工具面板
    traceview的面板分上下两个部分:

    • 时间线面板以每个线程为一行,右边是该线程在整个过程中方法执行的情况
    • 分析面板是以表格的形式展示所有线程的方法的各项指标

    时间线面板

    TraceView时间线面板
    左边是线程信息,main线程就是Android应用的主线程,这个线程是都会有的,其他的线程可能因操作不同而发生改变.每个线程的右边对应的是该线程中每个方法的执行信息,左边为第一个方法执行开始,最右边为最后一个方法执行结束,其中的每一个小立柱就代表一次方法的调用,你可以把鼠标放到立柱上,就会显示该方法调用的详细信息
    TraceView显示方法调用
    你可以随意滑动你的鼠标,滑倒哪里,左上角就会显示该方法调用的信息。
    1.如果你想在分析面板中详细查看该方法,可以双击该立柱,分析面板自动跳转到该方法
    TraceView显示详细信息
    2.放大某个区域
    刚打开的面板中,是我们采集信息的总览,但是一些局部的细节我们看不太清,没关系,该工具支持我们放大某个特殊的时间段
    TraceView放大某个区域
    如果想回到最初的状态,双击时间线就可以
    3.每一个方法的表示
    TraceView方法的表示
    可以看出来,每一个方法都是用一个凹型结构来表示,坐标的凸起部分表示方法的开始,右边的凸起部分表示方法的结束,中间的直线表示方法的持续

    分析面板

    面板列名含义如下

    名称 意义
    Name 方法的详细信息,包括包名和参数信息
    Incl Cpu Time Cpu执行该方法该方法及其子方法所花费的时间
    Incl Cpu Time % Cpu执行该方法该方法及其子方法所花费占Cpu总执行时间的百分比
    Excl Cpu Time Cpu执行该方法所话费的时间
    Excl Cpu Time % Cpu执行该方法所话费的时间占Cpu总时间的百分比
    Incl Real Time 该方法及其子方法执行所话费的实际时间,从执行该方法到结束一共花了多少时间
    Incl Real Time % 上述时间占总的运行时间的百分比
    Excl Real Time % 该方法自身的实际允许时间
    Excl Real Time 上述时间占总的允许时间的百分比
    Calls+Recur 调用次数+递归次数,只在方法中显示,在子展开后的父类和子类方法这一栏被下面的数据代替
    Calls/Total 调用次数和总次数的占比
    Cpu Time/Call Cpu执行时间和调用次数的百分比,代表该函数消耗cpu的平均时间
    Real Time/Call 实际时间于调用次数的百分比,该表该函数平均执行时间

    你可以点击某个函数展开更详细的信息
    TraceView函数展开
    展开后,大多数有以下两个类别:

    • Parents:调用该方法的父类方法
    • Children:该方法调用的子类方法

    如果该方法含有递归调用,可能还会多出两个类别:

    • Parents while recursive:递归调用时所涉及的父类方法
    • Children while recursive:递归调用时所涉及的子类方法

    首先我们来看当前方法的信息

    Name 24 android/widget/FrameLayout.draw(L android/graphics/Canvas;)V
    Incl Cpu% 20.9%
    Incl Cpu Time 375.201
    Excl Cpu Time % 0.0%
    Excl Cpu Time 0.000
    Incl Real Time % 1.1%
    Incl Real Time 580.668
    Excl Real Time % 0.0%
    Excl Real Time 0.000
    Calls+Recur 177+354
    Cpu Time/Call 0.707
    Real Time/Call 1.094

    根据下图中的toplevel可以看出总的cpu执行时间为1797.167ms,当前方法占用cpu的时间为375.201375.201/1797.167=0.2087,和我们的Incl Cpu Time%是吻合的。当前方法消耗的时间为580.668,而toplevel的时间为53844.141ms,580.668/53844.141=1.07%,和Incl Real Time %也是吻合的。在来看调用次数为177,递归次数为354,和为177+354=531375.201/531 = 0.7065Cpu Time/Call也是吻合的,580.668/531=1.0935,和Real Time/Call一栏也是吻合的
    TraceView计算

    • Parents
      现在我们来看该方法的Parents一栏
      TraceViewParents
    Name 22 com/android/internal/policy/impl/PhoneWindow$DecorView.draw(Landroid/graphics/Canvas;)V
    Incl Cpu% 100%
    Incl Cpu Time 375.201
    Excl Cpu Time %
    Excl Cpu Time
    Incl Real Time % 100%
    Incl Real Time 580.668
    Excl Real Time %
    Excl Real Time
    Call/Total 177/531
    Cpu Time/Call
    Real Time/Call

    其中的Incl Cpu Time%变成了100%,因为在这个地方,总时间为当前方法的执行时间,这个时候的Incl Cpu Time%只是计算该方法调用的总时间中被各父类方法调用的时间占比,比如Parents有2个父类方法,那就能看出每个父类方法调用该方法的时间分布。因为我们父类只有一个,所以肯定是100%Incl Real Time一栏也是一样的,重点是Call/Total,之前我们看当前方式时,这一栏的列名为Call+Recur,而现在变成了Call/Total,这个里面的数值变成了177/531,因为总次数为531次,父类调用了177次,其他531次是递归调用。这一数据能得到的信息是,当前方法被调用了多少次,其中有多少次是父类方法调用的

    • Children
      TraceViewChildren
      可以看出来,我们的子类有2个,一个是自身,一个是23android/view/View.draw(L android/graphics/Canvas;)Vself代表自身方法中的语句执行情况,由上面可以看出来,该方法没有多余语句,直接调用了其子类方法。另外一个子类方法,可以看出被当前方法调用了177次,但是该方法被其他方法调用过,因为他的总调用次数为892次,你可以点击进入子类方法的详细信息中

    • Parents while recursive
      TraceViewParents while recursive
      列举了递归调用当前方法的父类方法,以及其递归次数的占比,犹豫我们当前的方法递归了354次,以上三个父类方法递归的次数分别为348+4+2=354

    • Children while recursive
      TraceViewChildren while recursive
      列举了当递归调用时调用的子类方法

    Lint分析工具

    检测资源文件是否有没有用到的资源。
    检测常见内存泄露
    安全问题
    SDK版本安全问题
    是否有费的代码没有用到
    代码的规范—甚至驼峰命名法也会检测
    自动生成的罗列出来
    没用的导包
    可能的bug

    Analyze -> Inspect Code便可执行检查
    可以检查project,Module和指定文件
    Link分析工具
    详细信息
    Link分析工具分析界面

    展开全文
  • 内存分析(详解与代码)

    千次阅读 2020-05-09 14:57:55
    要想了解Java的低层是如何运作的,更扎实的明白Java的数据存储,内存分析是必不可少的。 Java虚拟机的内存可以分为三个区域:栈,堆和方法区(实际上是2个,方法区实际上是一种特殊的堆,存在堆里面),不管是堆,...

     要想了解Java的低层是如何运作的,更扎实的明白Java的数据存储,内存分析是必不可少的。

    Java虚拟机的内存可以分为三个区域:栈,堆和方法区(实际上是2个,方法区实际上是一种特殊的堆,存在堆里面),不管是堆,栈还是方法区,都有相应的特点,存放相应的东西。

    堆的特点:

    1、堆用于存储创建好的对象和数组(数组也是对象)

    2、JVM只有一个堆,被所有线程共享

    3、堆是一个不连续的内存空间,分配灵活,速度慢!

    栈的特点:

    1、栈描述的是方法执行的内存模型,每个方法被调用都会创建一个栈帧(存储局部变量、操作数、方法出口等)

    2、JVM为每个线程创建一个栈,用于存放该线程执行方法的信息(实际参数、局部变量等)

    3、栈属于线程私有,不能实现线程间的共享!

    4、栈的存储特点是:先进后出,后进先出

    5、栈是由系统自动分配,速度快!栈是一个连续的内存空间!

    方法区(又叫静态区)特点:

    1、JVM只有一个方法区,被所有线程共享!

    2、方法区实际也是堆,只是用于存储类、常量相关的信息!

    3、用来存放程序中永远是不变或唯一的内容。(类信息【Class对象】,静态变量、字符串常量等)

    下面我们就举个例子,说明一下内存分析,先贴下代码,我们有简单的3个类,公司类,部门类,员工类。 

     公司类:

    public class Company {
    	private String name = "我房旅居集团";			//8
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    	
    }

    部门类: 

    public class Department {
    
    	private String name;
    	private Company company;
    	
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    
    	public Company getCompany() {
    		return company;
    	}
    
    	public void setCompany(Company company) {
    		this.company = company;
    	}
    	
    	
    }

     员工类:

    public class Staff {
    
    	private static boolean ISANIMAL = true;
    	private String name;
    	private int age;
    	Department department;
    	
    	public String staffInformation(){
    		String string = "";			//16、23
    		//17、24
    		string = "用户为:"+department.getCompany().getName()+department.getName()+name;
    		return string;
    	}
    	public static boolean isAnimal(){
    		return ISANIMAL;			//19
    	}
    	//javac Staff.java		java Staff     				//1
    	public static void main(String[] args) {			//2
    		Staff staff = new Staff();						//3、4
    		staff.name = "林高禄";							//5
    		staff.age = 28;									//6
    		Company company = new Company();				//7、8、9
    		Department department = new Department();		//10、11
    		department.setCompany(company);					//12
    		department.setName("系统开发部");				//13
    		staff.department = department;					//14
    		String iInformation = staff.staffInformation();	//15、18
    		System.out.println(iInformation);				//19
    		System.out.println(Staff.isAnimal());			//20
    		company.setName("橙云科技");						//21
    		iInformation = staff.staffInformation();		//22、25
    		System.out.println(iInformation);				//26
    	}
    
    	
    }

    注:以上代码中的注释1-26,代表的是代码再内存分析中的执行步骤。

    下面我们就进行内存分析:

    第1步 :将java文件编译成class文件,类信息【Class对象】,静态变量、字符串常量等都存放到方法区中。

    第2步:main方法的栈帧执行,staff为空

    第3步:调用Staff类的构造器,先创建对象,属性赋值,后调用构造器赋值

    第4步:Staff类的构造器调完,栈帧出栈,堆内内存地址赋予staff

     第5步:staff通过内存地址找到name,把"林高禄"的地址赋予给staff.name

    第6步:staff通过内存地址找到age,把age赋值为28

      第7步:调用Company构造器

     第8步:把"我房旅居集团"的地址赋予给Company类的name

     第9步:Company类的构造器调完,栈帧出栈,堆内内存地址赋予company

    第10步:调用Department构造器

     第11步:Department类的构造器调完,栈帧出栈,堆内内存地址赋予department

     第12步:把company的内存地址赋予给department的company

     第13步:department通过内存地址找到name,把"系统开发部"的地址赋予给department.name

      第14步:把department的内存地址赋予给staff的department

    第15步:调用staffInformation方法入栈

    第16步:把“”的地址赋予给staffInformation方法内部的string

    第17步:通过一些列get方法加上字符串拼接,组装成新字符串,并且把新字符串的地址赋值给staffInformation方法内部的string

     第18步:staffInformation方法调完,栈帧出栈,把staffInformation方法返回的字符串地址赋予给iInformation,内部的string也被回收。

    第19步:打印iInformation

    第20步: 调用isAnimal方法入栈,返回值,调用完栈帧出栈,打印返回的值

    第21步: company通过内存地址找到name,把"橙云科技"的地址赋予给company.name(这里内存地址是不一样的,画图输入错误)

     第22步:调用staffInformation方法入栈

     第23步:把“”的地址赋予给staffInformation方法内部的string

     第24步:通过一些列get方法加上字符串拼接,组装成新字符串,并且把新字符串的地址赋值给staffInformation方法内部的string

     第25步:staffInformation方法调完,栈帧出栈,把staffInformation方法返回的字符串地址赋予给iInformation,内部的string也被回收。

     第26步:打印iInformation

    到这里,这个内存分析简单的介绍完了,如果有什么不对的地方,还请大家指出来。 

    展开全文
  • 内存分析工具

    千次阅读 2018-05-26 00:38:49
    内存分析工具1. 测试工具介绍1.1测试目的java内存泄漏是每个Java程序员都会遇到的问题,程序在本地运行一切正常,可是布署到远端就会出现内存无限制的增长,最后系统瘫痪,那么如何最快最好的检测程序的稳定性,防止系统...
  • 浅谈内存分析

    2012-03-10 00:18:10
    只知其表,不知其理。这是我们衡量一个人专业知识是否有深度的基本法则?作为初来乍到的程序猿来说,内存分析是我们编写速度快、效率高的代码必不可少的知识。...(一)先解释一下静态内存分析与动态内存分析
  • jmap、 jhat 分析内存溢出

    千次阅读 2017-03-04 18:34:01
    查看该进程下堆内存的使用情况 jmap -heap 1963(进程号) 查看存活对象的内存使用情况 jmap -histo:live 1963 ...3.还可以使用jhat分析内存溢出的原因 使用dump内存信息到heap.bin文件 jmap -dump:live
  • 网上资料中有很多说明,但是没有解析的太清楚,我这里分析汇总了一下。供大家参数。学习知识,要关注细节,懂与不懂差很多的呦!我说的是薪资:)。 Native Heap:Native代码分配的内存,虚拟机和Android框架分配...
  • linux下分析Java程序内存汇总

    万次阅读 2015-06-12 16:31:25
    使用pmap查看进程内存 运行命令 使用pmap可以查看某一个进程(非java的也可以)的内存使用使用情况, 命令格式: pmap 进程id 示例说明 例如运行: pmap 12358 显示结果如下图(内容较多, 分成几张图说明)...
  • Java学习1:图解Java内存分析详解(实例)

    万次阅读 多人点赞 2018-09-21 14:47:23
    Java学习1:图解Java内存分析详解(实例)
  • poi解决内存消耗过大溢出问题

    千次阅读 2016-03-18 08:03:50
    poi解析引擎的解析速度是非常快的,一般2000条数据200多毫秒就解析完成了,但是这带来的是巨大的内存消耗,当文件过大,或者多人同时使用这个引擎 这个时候就容易导致内存不足而溢出,使得java虚拟机抛出OutOfMemory...
  • 一次内存溢出的填坑经历

    千次阅读 2015-08-28 16:51:26
    在项目运行过程中,可能会出现内存溢出,内存溢出的原因多种多样,而在内存溢出后,我们如何查找和分析内存溢出的原因呢?这里来说一说我遇到的次遇到的内存溢出经历。 大致情况是这样的:应用在启动后,过一段时间...
  • nmon结果分析

    万次阅读 2016-04-11 20:16:59
    用nmon_analyser_hzt.xls等分析工具打开nmon结果文件,如果出现无法加载宏的提示,点击工具-宏-安全性,将安全及调至低,保存后,重新打开。 Ø Sys_summ页,为服务器资源使用率汇总 我们需求的主要数据为cpu,mem...
  • idea 可 以 分 析 存 快 照 分 析 吗 ? 如 果 可 以 怎 么 玩?
  • APP运行占用内存分析

    千次阅读 2015-09-13 21:34:46
    对于手机来说,运行内存还是挺珍贵的,因此,如果你的APP能够占用尽量少的内存,无疑会更加受用户欢迎。下面说说APP运行时占用内存分析: 测试方法: 一、使用Android Studio自带的工具Memory monitor,来测试。...
  • 对APP进行dumpsys meminfo内存分析解读

    万次阅读 2018-09-29 13:40:07
    使用adb shell dumpsys meminfo分析app内存截图: Native Heap:Native代码分配的内存,虚拟机和Android框架分配内存。关于什么是Native代码,即非Java代码分配的内存。详细介绍请找百度。 Dalvik Heap:Java...
  • 学习 Java 虚拟机能深入地理解 Java 这门语言,想要深入学习java的各种细节,很多时候你要深入到字节码层次去分析,你才能得到准确的结论,通过学习JVM你了解JVM历史,内存结构、垃圾回收机制、性能监控工具、认识类...
  • Android中如何查找内存泄露

    万次阅读 2013-03-11 17:16:46
    1、首先确定是否有内存泄露及哪个程序造成。 1.1、内存泄露已弹出out of memory对话框的情况。 这种情况很简单,直接看对话框就知道是哪个应用的问题了。然后再分析该应用是否是因为内存泄露造成的out of memory...
  • Xcode 内存分析工具使用

    千次阅读 2016-01-17 00:03:54
    Xcode 内存分析有静态分析和动态分析,静态分析主要是语法,会自动找出不合理的代码,提醒我们需要在某个地方优化,但是这个有的时候并不准确,该提醒的不提醒。 使用方法是 在Xcode 选择 Product ->Analyze就...
  • 分析较大的dump文件(根据我自己的经验2G以上的dump文件就需要使用以下介绍的方法,不然mat会出现oom)需要调整虚拟机参数  找个64位的系统在MemoryAnalyzer.ini设置-Xmx2g  如果是32位的xp可以使用下面的方法...
1 2 3 4 5 ... 20
收藏数 1,360,380
精华内容 544,152
关键字:

内存分析