Foreword
This series of articles:
1, by way of explanation easy to understand, explain the practical value of a technology
2, a detailed written source tracking, source code configuration diagram shots, drawing class, try to explain in detail the principles of the exploration process
3 provides Github can run Demo project, but I have provided the code, provide more ideas, better ideas, please, as appropriate, CV
4, finishing a set of principles in the process of exploring some pit, or precautions during operation in the demo
5, gif map with the most intuitive operating results show demo
If you think the details are too thin, you can skip to see the conclusion. Limited capacity, should the description found inappropriate, welcome messages criticism.
And learn to live to old, long road repair come far. And all the king of mutual encouragement!, With me as a CV engineer it, manually funny
Body outline
- jvm memory management knowledge
- Detecting and processing the jitter memory
- And a processing memory leak detection
text
jvm memory management knowledge
LMK (LowMemoryKill) mechanism android bottom will be when the system ran out of memory, according to certain rules to kill some processes to meet the memory needs of other processes. Which consume memory which is an indicator of the level, so the app's memory footprint optimization, can effectively reduce the probability of being killed app system.
GC STW mechanism GC, garbage collection process, atGC
the time of task execution thread, there will be aSTW (stop the world)
mechanism, he will put all the other threads are suspended. If theGC
call very frequently, it will lead to the main thread is not smooth, giving the user the feeling that Caton.
Memory jitter frequent cause OOM memory jitter too often, frequently resulting in a large number of objects created and destroyed, can not produce large amounts of contiguous memory space, at this time if there is a need to apply large object memory, it is possible to apply fail, leading toOOM
memory overflow
sentence explanation memory leak long life cycle of an object held by the short life cycle of a strong reference to the object and found that can not be recycled, regarded as leakage at the time of the short life cycle of an object to be recovered
GC recovery reachability analysis
GC thread can determine whether an object is recovered, is reachability analysis algorithm, calculatedGcRoot
fromGcRoot
the search down, theGcRoot
entire object is not directly related to garbage collection.
Strong weak references virtual four strong and imaginary without saying. The most common strong, there is no special treatment are strong references (including anonymous inner class will hold a strong reference to the outer class). Phantom reference no use, will not be discussed. Soft references, as well as to define some use, but does not have an object, useSoftRefrence<T>
modified, in memory stressful time, GC recycling after useSoftRefrence<T>
Modification of the object if there is enough system memory is available, then the associated soft references will not be recovered. If insufficient, the soft recovery of the associated object reference. Weak references (WeakRefrence<T>
), weaker than the soft reference number, as long as the GC trigger object associated with weak references will be recovered.
Note: Use soft and weak references, to determine whether the associated object is empty.
Jitter detection and treatment of memory
we use s development, we usually run the app, usually point RunApp
, but there is another choice, that is profileApp
, after you run the app it will see after clicking at the bottom of the window profile as, as will appear below profile FIG displayed in the network, the use of memory and cpu
If the memory of the figure shake is very obvious, such as ECG like this:
it shows that there is a very clear memory of jitter in urgent need of treatment: After clicking the graphics area of memory, you can see detailed changes in memory, and memory allocation:
here is a pit:
If you look from the drawing to smooth the trend memory, and does not appear on the full simulation of jitter figure so exaggerated, there is no memory jitter is not it? It is not. Because our gc, in the case of memory will be unavailable to reclaim memory, if the app take up memory has been relatively small, did not touch the critical value gc, then they will not appear cliff-style down. So this would not observe memory jitter, and how to do it?
解决方法 在8.0以下的安卓手机上,在下方的位置上会出现一个Record按钮(如果是8.0以上,你可以直接用拖拽的方式来截取一段内存record):
点击它,一段时间之后,再点一下:你就能在下方发现一张表格:
这张表格代表的是,你Record这段时间之内创建的对象,点击一下第二列 Allocations
,对创建的数量进行排序,找出创建次数最多的对象:
然后,点击排行第一的String之后,会在右方看到
然后点击其中的一个,又会看到一个新的窗口:
此为止,就找到了 创建对象 的 元凶,以这个为线索,找到你们自己包名下的类和方法,确定是我们自己的代码在不合理地创建对象.
再往后,就是根据各自的业务代码去做优化了,记住一个宗旨:不要让代码干多余的事。 如果是我们调用了系统的api导致了不合理地大量对象的创建,那么就要考虑这个系统API为什么会这样创建对象,有没有其他方法避免吗,从业务代码层来合理使用这个api,实在不行再考虑自定义api或者换个系统api。
在我们做了一次优化之后,再profile运行一次app,再重复上面的过程。以此类推,直到内存抖动达到理想状态。
总结
优化内存抖动,核心就是防止频繁创建对象。常见的反面教材就是:循环中创建对象,大量调用的api中创建对象。而优化的主要手段,就是对象复用,常见的手段是:对象池,像是 Handler的Message 单链表池,Glide的bitmap池等。
检测以及处理内存泄漏
经典案例: 处理 handler异步任务导致的内存泄漏方法
- 在Activity的
onDestroy
中移除所有的任务@Override protected void onDestroy() { super.onDestroy(); handler.removeCallbacksAndMessages(null);//移除所有任务 }
- 使用静态内部类 + Activity弱引用的方式
MyHandler handler = new MyHandler(this); private static class MyHandler extends Handler { WeakReference<Activity> activityWeakReference; MyHandler(Activity activity) { activityWeakReference = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { //在执行任务的时候,判断弱引用所关联的对象是否为空,能在对象已经被回收的情况下 switch (msg.what) { case 1: if (activityWeakReference.get() !=null) { //T0D0 } } } }
工具的使用
依然是 profileApp
,先用 profile看出内存的变化情况。
问:如何判断内存泄漏?
答:内存泄漏是精细功夫,不能全盘观察,只能凭借profile的内存变化来推测。比如,打开app之后内存一路飙升,直到超出app能够使用的最大内存,app崩溃,,这是最明显的。又比如,你反复打开关闭某一个界面,发现内存的稳定线( 内存稳定之后,内存占用值)随着每一次的打开关闭,都在提高,这说明,这一个界面上存在泄漏,有对象无法被回收。
上一章节使用 profile
最多是了解到 哪些对象的创建和回收引起了内存抖动,但是,涉及到泄漏,只通过profile尚且 不能知道是 哪个类持有了希望被回收的对象的强引用. 这里就要借助另外一款工具,他的名字叫做 EclipseMat
(自行百度)
先回到刚才的 profile
点一下,然后再点一下,界面会自动跳转:
点击上面的保存按钮,将文件存到本地;
然后:
但是这个文件是无法直接在mat打开的
找到SDK
目录下的要 hprof-conv.exe
:
使用cmd
命令,对文件进行转换,命令为:hprof-conv
[源文件名][目标文件名]如 hprof-conv1.hprof2.hprof
回车
将得到的 2.hprof
利用刚才下载的Mat工具打开:
这里有很多指标,但是检查内存泄漏,我们只需要关注这个直方图按钮即可:
这个图中会列出你dump的这一段内存中的所有对象,包括framework层的,也包括我们自己代码创建的对象
案例模拟
我模拟了一个经典案例,也就是前面提到的 Handler延时任务导致 Activity不能被释放,核心代码如下
public class SecondActivity extends AppComatActivity {
Handler handler = new Handler();
//创建一个强引用Activity的handler对象
@Override
protected void onCreate (Bundle savedInstancestate) {
super.onCreate(saveInstanceState);
setContentView(R.layout.activity_second);
handler.postDelayed(new Runnable() {
@Override
public void run() {
}
}, Integer.MAX_VALUE);
//我让任务永远在这里
}
我就用一个非常普通的方式创建了一个 handler
对象,并且用它来执行一段延时任务,只不过,延时任务的延时时间是 Integer
的最大值,也就是说,任务要很久以后才会执行。之后,我反复进出这一个 Activity
,然后按照上面的方式 dump
了一段 hprof
,经过 hprof-conv
转化,然后用 Mat
打开:结果如下
我填写过滤信息: SecondActivity
回车
在我们最终退出SecondActivity
之后,内存中依然保留了 18个无用的对象。
那么是不是我们这18个都是泄漏的呢?
不一定
前文讲过,只有不合理的强引用,才会导致内存泄漏,所以我们要按照上面的方式排除软弱虚引用。之后我们能看到下面的界面,把能展开的信息尽数展开
了解 Handler
源码的同志们应该一眼就看明白了, handler
引起了内存泄漏,是因为存在不合理地强引用链, 上图中可以看出,最终是callback
对象持有了 SecondActivity
对象。
如何优化内存泄漏
我们刚才已经看到了Handler的不合理使用导致了内存泄漏,那么如果在 onDestroy
中移除所有的任务
@Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacksAndMessages(token.null):
}
}
执行同样的任务,dump
下来的hprof
在mat触发了GC
之后, SecondActivity
数量变为了0,内存泄漏解决。
当然还有另一种做法,静态内部类+弱引用。
ps: 静态内部类是为了防止内部类持有外部类的引用,弱引用是为了在
GC
触发之时,回收掉WeakRefrence
中的对象。public class secondActivity extends AppCompatActivity { Handler handler = new Handler(): @Override protected void onCreate(Bundle saveInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); handler.postDelayed(runnable, Integer.MAX_VALUE); //依旧是那个延时很久的任务 } Runnable runnable = new MyRunnable(this); private static class MyRunnable implements Runnable { //静态内部类 WeakReference<activity> activityWeakReference //弱引用 MyRunnable(Activity activity) { activityWeakReference = new WeakReference<>(activity); } @Override public void run() { } }
但是排除之后,一个都没有了。
小技巧
The above steps, while feasible, but if there are many pages need to troubleshoot the leak, then we go a point to open a page closed, the whole process will be very long uncomfortable. In fact, there is a solution. Histogram before back:
use is: if you want an operation, before and after each operation you dump a hprof
named before and after, and then hprof-conv
convert it, into a before and the After ` ,用
the Eclipse MAT 同时打开这两个文件,然后切换到
after.hprof`, click button image above
It will let you select the file you want to compare, click on before, then filtered SecondActivity
in this way may leak prior to processing, prior to troubleshoot code region may leak. Simplify our optimization work.
Epilogue
Memory leaks and optimizing the jitter Jvm involves a lot of knowledge, in addition to the points I listed earlier, there are a lot of minutiae. To do memory optimization, JVM needs a solid knowledge base.