adb shell dumpsys meminfo [pkg/pid] 可以用来查看指定进程包名的内存使用情况
RAM(random access memory): 随机存取存储器, 就是内存。
寄存器(Registers):速度最快的存储场所,因为寄存器位于处理器内部,我们在程序中无法控制
栈(Stack):存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中
堆(Heap):堆内存用来存放由new创建的对象和数组。在堆中分配的内存,由Java虚拟机的自动垃圾回收器(GC)来管理。
静态域(static field):静态存储区域就是指在固定的位置存放应用程序运行时一直存在的数据,Java在内存中专门划分了一个静态存储区域来管理一些特殊的数据变量如静态的数据变量
常量池(constant pool):虚拟机必须为每个被装载的类型维护一个常量池。常量池就是该类型所用到常量的一个有序集和,
包括直接常量(string,integer和floating point常量)和对其他类型,字段和方法的符号引用。
非RAM存储:硬盘等永久存储空间
- 虚拟内存:进程空间内的虚拟内存地址 。
- 物理内存:就是真正写的到内存条上的,真实地址对进程不可见,由操作系统把虚拟内存地址映射到物理内存地址。
- Size:指的就是分配了多少虚拟内存
- Rss、Pss指的是实际物理内存使用的大小,由于这个内存段是纯new出来的,没有共享库,所以这两个值是一样的。由于只给4MB的数组赋值,操作系统只给分配了4MB的真实物理内存。
- Objects是统计App内部组件对象个数,其中Views、ViewRootImpl以及Activities个数,在Activity onDestroy后应该都会回收清零,如果onDestroy调用后这几个对象个数没有清零,就可能发生了内存泄漏。
- android程序内存被分为2部分:native和dalvik,dalvik就是java堆,普通java对象是在java堆分配,而bitmap是直接在native上分配,对于内存的限制是 native+dalvik 不能超过最大限制。
查看单个应用最大内存限制 :adb shell getprop |grep heapgrowthlimit
内存 adb shell dumpsys meminfo
CPU adb shell dumpsys cpuinfo
帧率 adb shell dumpsys gfxinfo
显示 adb shell dumpsys display
Asset Allocations
: 168K
Applications Memory Usage (in Kilobytes):
//Uptime 表示启动到现在的时长,不包含休眠的时间,单位毫秒(ms)
//Realtime 表示启动到现在的时长,包含休眠的时间,单位毫秒(ms)
Uptime: 48836081 Realtime: 169360016
Pss Total : 占用了真实的物理内存的空间
private dirty : 指私有驻留内存
Heap Size: 指占用总内存(Heap 堆)
Heap Alloc : 指在虚拟地址中分配了这么多空间
Heap Free: 空闲内存
** MEMINFO in pid 12056 [com.xxx.android.app.camera] **
Pss Private Private SwapPss Heap Heap Heap
Total Dirty Clean Dirty Size Alloc Free
------ ------ ------ ------ ------ ------ ------
//Native Heap :c 中malloc出来的堆空间,扩展:c++申请的内存为native process,java申请的内存:java process
//camera中一般是算法申请的内存,或者自己写的另外一些 jni的方法
Native Heap 50032 49388 620 21577 81116 65779 15336
//Dalvik Heap:指java中new出来的java堆空间,占用的虚拟内存的空间
Dalvik Heap 35836 34948 792 16 25747 19311 6436
Dalvik Other 1377 1336 40 417
Stack 44 36 8 20
Ashmem 2 0 0 0
Gfx dev 4116 4116 0 0
Other dev 190 112 32 0
.so mmap 6292 812 3516 11
.jar mmap 1879 0 688 0
.apk mmap 3842 0 3268 0
.ttf mmap 6 0 0 0
.dex mmap 9 8 0 0
.oat mmap 826 0 124 0
.art mmap 2729 1500 564 71
Other mmap 768 276 4 0
EGL mtrack 3456 3456 0 0
GL mtrack 44720 44720 0 0
Unknown 1867 1368 480 1674
TOTAL 181777 142076 10136 23786 106863 85090 21772
App Summary
Pss(KB)
------
Java Heap: 37012
Native Heap: 49388
Code: 8416
Stack: 36
Graphics: 52292
Private Other: 5068
System: 29565
TOTAL: 181777 TOTAL SWAP PSS: 23786
Objects
Views: 67 ViewRootImpl: 1
AppContexts: 7 Activities: 1
Assets: 13 AssetManagers: 0
Local Binders: 26 Proxy Binders: 43
Parcel memory: 16 Parcel count: 66
Death Recipients: 2 OpenSSL Sockets: 0
WebViews: 0
Java:从Java或Kotlin代码分配的对象内存。
Native:从C或C ++代码分配的对象内存。
即使您的应用中不使用C ++,您也可能会看到此处使用的一些原生内存,因为Android框架使用原生内存代表您处理各种任务,如处理图像资源和其他图形时,即使您编写的代码采用Java或Kotlin语言。
图形:图形缓冲区队列向屏幕显示像素(包括GL表面,GL纹理等等)所使用的内存。(请注意,这是与CPU共享的内存,不是GPU专用内存。)
Stack:您的应用中的原生堆栈和Java堆栈使用的内存。这通常与您的应用运行多少线程有关。
代码:您的应用用于处理代码和资源(如dex字节码,已优化或已编译的dex码,.so库和字体)的内存。
其他:您的应用使用的系统不确定如何分类的内存。
分配:您的应用分配的Java / Kotlin对象数。它没有计入C或C ++中分配的对象。
当连接至运行Android 7.1及更低版本的设备时,此分配仅在Memory Profiler连接至您运行的应用时才开始计数。因此,您开始分析之前分配的任何对象都不会被计入。不过, Android 8.0附带一个设备内置分析工具,该工具可记录所有分配,因此,在Android 8.0及更高版本上,此数字始终表示您的应用中待处理的Java对象总数。
这个图形也就是graphics这个怎么越来越多呢?如何处理
Dalvik:Dalvik是Google公司自己设计用于Android平台的Java虚拟机。
它可以支持已转换为.dex格式的Java应用程序的运行,.dex格式是专门为Dalvik应用设计的一种压缩格式,
适合内存和处理器速度有限的系统,Dalvik经过优化,允许在有限的内存中同时运行多个虚拟机实例,并且每一个Dalvik应用做为独立的Linux进程执行,
独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭。
ART:ART表示Android Runtime,Dalvik是依靠一个just-In -Time编译器去解释字节码,运行时编译后的应用都需要通过一个解释器在用户的设备上运行,
这一机制并不是特别高效,但是能让应用更容易在不同的硬件和架构上运行。ART则是完全改变了这种做法,在安装应用的时候就预编译字节码到机器语言,
这一机制叫预编译。在移除解释代码这一过程,应用程序执行将更有效率,启动速度更快。
JVM内存的划分有五片:
1. 寄存器;
2. 本地方法区;
3. 方法区;
4. 栈内存;
5. 堆内存。
程序计数器:存储当前线程执行目标方法执行到第几行。
本地方法栈:本地方法栈主要是为执行本地方法服务的。而Java栈是为执行Java方法服务的。
方法区:该区域被线程共享。主要存储每个类的信息(类名,方法信息,字段信息等)、静态变量,常量,以及编译器编译后的代码等。
栈内存:Java栈中存放的是一个个栈帧,每个栈帧对应一个被调用的方法。栈帧包括局部标量表,
操作数栈。
堆内存:Java中的堆是被线程共享的,且JVM中只有一个堆内存,主要存储对象本身及数组
栈内存:栈内存首先是一片内存区域,存储的都是局部变量,凡是定义在方法中的都是局部变量(方法外的是全局变量),for循环内部定义的也是局部变量,是先加载函数才能进行局部变量的定义,所以方法先进栈,然后再定义变量,变量有自己的作用域,一旦离开作用域,变量就会被释放。栈内存的更新速度很快,因为局部变量的生命周期都很短。
堆内存:存储的是数组和对象(其实数组就是对象),凡是new建立的都是在堆中,堆中存放的都是实体(对象),实体用于封装数据,而且是封装多个(实体的多个属性),如果一个数据消失,这个实体也没有消失,还可以用,所以堆是不会随时释放的,但是栈不一样,栈里存放的都是单个变量,变量被释放了,那就没有了。堆里的实体虽然不会被释放,但是会被当成垃圾,Java有垃圾回收机制不定时的收取。
举例:
比如主函数里的语句 int [] arr=new int [3];在内存中是怎么被定义的:
主函数先进栈,在栈中定义一个变量arr,接下来为arr赋值,
但是右边不是一个具体值,是一个实体。实体创建在堆里,在堆里首先通过new关键字开辟一个空间,内存在存储数据的时候都是通过地址来体现的,
地址是一块连续的二进制,然后给这个实体分配一个内存地址。数组都是有一个索引,数组这个实体在堆内存中产生之后每一个空间都会进行默认的初始化
(这是堆内存的特点,未初始化的数据是不能用的,但在堆里是可以用的,因为初始化过了,但是在栈里没有),
不同的类型初始化的值不一样。所以堆和栈里就创建了变量和实体:
那么堆和栈是怎么联系起来的呢?
我们刚刚说过给堆分配了一个地址,把堆的地址赋给arr,arr就通过地址指向了数组。所以arr想操纵数组时,就通过地址,
而不是直接把实体都赋给它。这种我们不再叫他基本数据类型,而叫引用数据类型。称为arr引用了堆内存当中的实体。
(可以理解为c或c++的指针,Java成长自c++和c++很像,优化了c++)
如果当int [] arr=null;
arr不做任何指向,null的作用就是取消引用数据类型的指向。
当一个实体,没有引用数据类型指向的时候,它在堆内存中不会被释放,而被当做一个垃圾,在不定时的时间内自动回收,因为Java有一个自动回收机制,(而c++没有,需要程序员手动回收,如果不回收就越堆越多,直到撑满内存溢出,所以Java在内存管理上优于c++)。自动回收机制(程序)自动监测堆里是否有垃圾,如果有,就会自动的做垃圾回收的动作,但是什么时候收不一定。
所以堆与栈的区别很明显:
1.栈内存存储的是局部变量而堆内存存储的是实体;
2.栈内存的更新速度要快于堆内存,因为局部变量的生命周期很短;
3.栈内存存放的变量生命周期一旦结束就会被释放,而堆内存存放的实体会被垃圾回收机制不定时的回收
2、Java内存回收算法
2.1判断Java中对象是否存活的算法
2.1.1 引用计数算法
堆内存的每个对象都有一个引用计数器,当对象被引用的时候,计数器+1,当引用失效时计数器-1,当计数器的值为0时,说明该对象没有被引用,就会被认为是垃圾对象,系统将会将其回收内存重新分配。
优点:引用计数器执行简单,判定效率高。
缺点:对于循环引用的对象难以判断出来,同时引用计数器增加了程序执行的开销,在jdk1.1后,就不在使用了。
2.1.1 根搜索法
GC Roots的对象做为起点,然后向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则该对象不可达,也就是说该对象为为垃圾对象,可以被回收。
在Java中,可以做为GC Roots的对象包括一下四种:
1.虚拟机栈中引用的对象
2.方法区中的类静态属性引用的对象
3.方法区中常量引用的对象
4.本地方法栈中JNI的引用对象
MAT使用:
MAT下载地址:https://www.eclipse.org/mat/downloads.php