AndroidStudioProfilerMemory(内存分析⼯具)的简单使⽤及问题分析
Memory Profiler 是 Android Studio⾃带的内存分析⼯具,可以帮助开发者很好的检测内存的使⽤,在出现问题时,也能⽐较⽅便的分析定位问题,不过在使⽤的时候,好像并⾮像⾃⼰⼀开始设想的样⼦。
如何查看整体的内存使⽤概况
如果想要看⼀个APP整体内存的使⽤,看APP heap就可以了,不过需要注意Shallow Size跟Retained Size是意义,另外native消耗的内存是不会被算到Java堆中去的。
Allocations:堆中的实例数。
Shallow Size:此堆中所有实例的总⼤⼩(以字节为单位)。其实算是⽐较真实的java堆内存
Retained Size:为此类的所有实例⽽保留的内存总⼤⼩(以字节为单位)。这个解释并不准确,因为Retained Size会有⼤量的重复统计
native size:8.0之后的⼿机会显⽰,主要反应Bitmap所使⽤的像素内存(8.0之后,转移到了native)
举个例⼦,创建⼀个List的场景,有⼀个ListItem40MClass类,⾃⾝占⽤40M内存,每个对象有个指向下⼀个ListItem40MClass对象的引⽤,从⽽构成List,
class ListItem40MClass {
byte[] content = new byte[1000 * 1000 * 40];
ListItem40MClass() {
for (int i = 0; i < content.length; i++) {
content[i] = 1;
}
}
@Override
protected void finalize() throws Throwable {
super.finalize();
}
ListItem40MClass next;
}
@OnClick(R.id.first)
void first() {
if (head == null) {
head = new ListItem40MClass();
} else {
ListItem40MClass tmp = head;
while ( != null) {
tmp = ;
}
< = new ListItem40MClass();
}
}
我们创建三个这样的对象,并形成List,⽰意如下
A1->next=A2
A2->next=A3
A3->next= null
android最新版这个时候⽤Android Profiler查看内存,会看到如下效果:Retained Size统计要⽐实际3个ListItem40MClass类对象的⼤⼩⼤的多,如下图:
可以看到就总量⽽⾔Shallow Size基本能真是反应Java堆内存,⽽Retained Size却明显要⾼出不少, 因为Retained Size统计总内存的时候,基本不能避免重复统计的问题,⽐如:A对象有B对象的引⽤在计算总的对象⼤⼩的时候,⼀般会多出⼀个B,就像上图,有个3个约
40M的int[]对象,占内存约120M,⽽每个ListItem40MClass对象⾄少会再统计⼀次40M,这⾥说的是⾄少,因为对象间可能还有其他关系。我们看下单个类的内存占⽤-Instance View
Depth:从任意 GC 根到所选实例的最短 hop 数。
Shallow Size:此实例的⼤⼩。
Retained Size:此实例⽀配的内存⼤⼩(根据 dominator 树)。
可以看到Head本⾝的Retained Size是120M ,Head->next 是80M,最后⼀个ListItem40MClass对象是40M,因为每个对象的Retained Size除了包括⾃⼰的⼤⼩,还包括引⽤对象的⼤⼩,整个类的Retained Size⼤⼩累加起来就⼤了很多,所以如果想要看整体内存占⽤,看Shallow Size还是相对准确的,Retained Size可以⽤来⼤概反应哪种类占的内存⽐较多,仅仅是个⽰意,不过还是Retained Size⽐较常⽤,因为Shallow Size的⼤户⼀般都是String,数组,基本类型意义不⼤,如下。
FinalizerReference⼤⼩跟内存使⽤及内存泄漏的关系
之前说Retained Size是此实例⽀配的内存⼤⼩,其实在Retained Size的统计上有很多限制,⽐如Depth:从任意 GC 根到所选实例的最短hop数,⼀个对象的Retained Size只会统计Depth⽐⾃⼰⼤的引⽤,⽽不会统计⼩的,这个可能是为了避免重复统计⽽引⼊的,但是其实Retained Size在整体上是免不了重复统计的问题,所以才会右下图的情况:
FinalizerReference中refrent的对象的retain size是40M,但是没有被计算到FinalizerReference的retain size中去,⽽且就图表⽽⾔FinalizerReference的意义其实不⼤,FinalizerReference对象本⾝占⽤的内存不⼤,其次FinalizerReference的retain size统计的可以说是FinalizerReference的重复累加的和,并不代表其引⽤对象的⼤⼩,仅仅是ReferenceQueue queue中ReferenceQueue的累加,
public final class FinalizerReference<T> extends Reference<T> {
// This queue contains those objects eligible for finalization.
public static final ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
// Guards the list (not the queue).
private static final Object LIST_LOCK = new Object();
// This list contains a FinalizerReference for every finalizable object in the heap.
// Objects in this list may or may not be eligible for finalization yet.
private static FinalizerReference<?> head = null;
/
/ The links used to construct the list.
private FinalizerReference<?> prev;
private FinalizerReference<?> next;
// When the GC wants something finalized, it moves it from the 'referent' field to
// the 'zombie' field instead.
private T zombie;
public FinalizerReference(T r, ReferenceQueue<? super T> q) {
super(r, q);
}
@Override public T get() {
return zombie;
}
@Override public void clear() {
zombie = null;
}
public static void add(Object referent) {
FinalizerReference<?> reference = new FinalizerReference<Object>(referent, queue);
synchronized (LIST_LOCK) {
reference.prev = null;
< = head;
if (head != null) {
head.prev = reference;
}
head = reference;
}
}
public static void remove(FinalizerReference<?> reference) {
synchronized (LIST_LOCK) {
FinalizerReference<?> next = ;
FinalizerReference<?> prev = reference.prev;
< = null;
reference.prev = null;
if (prev != null) {
< = next;
} else {
head = next;
}
if (next != null) {
next.prev = prev;
}
}
}
...
}
每个FinalizerReference retained size 都是其next+ FinalizerReference的shallowsize,反应的并不是其refrent对象内存的⼤⼩,如下:
因此FinalizerReference越⼤只能说明需要执⾏finalize的对象越多,并且对象是通过强引⽤被持有,等待Deamon线程回收。可以通过该下代码试验下:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论