The hand and take you to optimize memory leaks and jitter

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.
The hand and take you to optimize memory leaks and jitter
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, at GCthe time of task execution thread, there will be a STW (stop the world)mechanism, he will put all the other threads are suspended. If the GCcall 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 to OOMmemory 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, calculated GcRootfrom GcRootthe search down, the GcRootentire 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, use SoftRefrence<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
The hand and take you to optimize memory leaks and jitter
The hand and take you to optimize memory leaks and jitter

If the memory of the figure shake is very obvious, such as ECG like this:
The hand and take you to optimize memory leaks and jitter
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:
The hand and take you to optimize memory leaks and jitter
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):
The hand and take you to optimize memory leaks and jitter
点击它,一段时间之后,再点一下:你就能在下方发现一张表格:
The hand and take you to optimize memory leaks and jitter
这张表格代表的是,你Record这段时间之内创建的对象,点击一下第二列 Allocations,对创建的数量进行排序,找出创建次数最多的对象:
The hand and take you to optimize memory leaks and jitter
然后,点击排行第一的String之后,会在右方看到
The hand and take you to optimize memory leaks and jitter
然后点击其中的一个,又会看到一个新的窗口:
The hand and take you to optimize memory leaks and jitter
此为止,就找到了 创建对象 的 元凶,以这个为线索,找到你们自己包名下的类和方法,确定是我们自己的代码在不合理地创建对象.

再往后,就是根据各自的业务代码去做优化了,记住一个宗旨:不要让代码干多余的事。 如果是我们调用了系统的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
The hand and take you to optimize memory leaks and jitter
点一下,然后再点一下,界面会自动跳转:
The hand and take you to optimize memory leaks and jitter
The hand and take you to optimize memory leaks and jitter
点击上面的保存按钮,将文件存到本地;

然后:
The hand and take you to optimize memory leaks and jitter
但是这个文件是无法直接在mat打开的

找到SDK目录下的要 hprof-conv.exe
The hand and take you to optimize memory leaks and jitter

使用cmd命令,对文件进行转换,命令为:hprof-conv[源文件名][目标文件名]如 hprof-conv1.hprof2.hprof回车

将得到的 2.hprof利用刚才下载的Mat工具打开:
The hand and take you to optimize memory leaks and jitter
这里有很多指标,但是检查内存泄漏,我们只需要关注这个直方图按钮即可:The hand and take you to optimize memory leaks and jitter

这个图中会列出你dump的这一段内存中的所有对象,包括framework层的,也包括我们自己代码创建的对象
The hand and take you to optimize memory leaks and jitter

案例模拟

我模拟了一个经典案例,也就是前面提到的 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打开:结果如下
The hand and take you to optimize memory leaks and jitter
我填写过滤信息: SecondActivity 回车
The hand and take you to optimize memory leaks and jitter
在我们最终退出SecondActivity之后,内存中依然保留了 18个无用的对象。

那么是不是我们这18个都是泄漏的呢?
不一定
The hand and take you to optimize memory leaks and jitter
前文讲过,只有不合理的强引用,才会导致内存泄漏,所以我们要按照上面的方式排除软弱虚引用。之后我们能看到下面的界面,把能展开的信息尽数展开
The hand and take you to optimize memory leaks and jitter
了解 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&lt;activity&gt; activityWeakReference
//弱引用
MyRunnable(Activity activity) {
activityWeakReference = new WeakReference&lt;&gt;(activity);
}
@Override
public void run() {
}
}

The hand and take you to optimize memory leaks and jitter
The hand and take you to optimize memory leaks and jitter
但是排除之后,一个都没有了。

小技巧

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:
The hand and take you to optimize memory leaks and jitter
use is: if you want an operation, before and after each operation you dump a hprofnamed before and after, and then hprof-convconvert 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
The hand and take you to optimize memory leaks and jitter
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.

Guess you like

Origin blog.51cto.com/14541311/2458099