内存泄漏的实际例子

我一直在努力识别内存泄漏。 我想我的项目循环进度视图中有几个内存泄漏。

我的猜测之一是我有内部类FadeRunnable内存泄漏。 但说实话,我不知道如何确定这是否是问题的根源。 那么,当我做通常的情况下,切换方向,我看到增加的内存使用情况如下所示。 如果我注释掉FadeRunnable类的用法,步骤会更小( 但仍然存在,所以我想这不是唯一的泄漏

记忆步骤

一旦我分析堆转储,我看到一些东西。 但实际上我不知道这些价值是什么意思。 我做的事情是

  1. 多次改变方向
  2. 打开堆转储并按“保留大小”
  3. 现在当我点击“CircularProgressView”时,我在右边看到了8行,我想这意味着有8个“CircularProgressView”实例泄漏,并且在内存中孤立地存在。

它是否正确? 如果是这样,我怎么能find转储信息( 我猜在下面的窗格中的某个地方 )保存/保存此对象的位置。

堆转储

我很想有一个一步一步的解释如何找出是否和哪个对象泄漏一些内存。

所有怀疑视图的代码都可以在这个类中find。

https://github.com/momentummodules/CircularProgressView/blob/master/circularprogressview/src/main/java/momentum/circularprogressview/CircularProgressView.java

但也可以随时查看完整的项目,以获得更深入的洞察力,如果你想玩它。

提前致谢!

UPDATE

上面的代码链接显示了mem-leaking内部类的固定代码。 下面的代码片段显示了永远不会这样使用的原始的泄漏代码

 /** * Mem-leaking code, for fixed code see repository link * https://github.com/momentummodules/CircularProgressView/blob/master/circularprogressview/src/main/java/momentum/circularprogressview/CircularProgressView.java */ public class CircularProgressView extends View { ... private Thread fadeThread = null; ... ... class FadeRunnable implements Runnable { @Override public void run() { ... } } ... ... private void startFade(boolean fadeIn) { // check existing if(this.fadeThread != null) { // check if fade is already running switch(this.fadeThread.getState()) { case TERMINATED: case NEW: this.fadeThread = null; break; case RUNNABLE: case BLOCKED: case TIMED_WAITING: case WAITING: return; } } // create new this.fadeThread = new Thread(new FadeRunnable(fadeIn, this.fadeTime)); this.fadeThread.start(); } } 

Solutions Collecting From Web of "内存泄漏的实际例子"

是的,您在FadeRunnable类中有内存泄漏。

内部类的每个实例都包含对其外部类的隐式引用,可以通过OuterClass.this运算符访问。 在你的项目中,当你执行FadeRunnable ,然后通过方向改变触发重新configuration时,整个活动和你的CircularProgressView被重新创build,但是来自previous的FadeRunnable仍然活着(分配),并且由于它持有对其外部的隐式引用CircularProgressView类继续生存,这就是为什么经过多次重新configuration后,在内存中分配了8个CircularProgressView实例,并且情况变得更糟 – 每个View都保留对其上下文的引用,并且这也无法被释放,导致内存不良泄漏。

Runnables,Handlers和其他类似的对象,可以将它们的封装活动,片段,视图等等声明为标准类或STATIC内部类(静态内部类不保留对其外部类的隐式引用)不要保留ContextView等引用,而是保留一个WeakReference<>所以当你通过configuration改变重新创buildActivity时, View可以被垃圾收集器销毁和释放。

这是一个关于这个问题的非常丰富的文章,我强烈build议阅读它。

我想你有正确的方向。 这个FadeRunnable肯定不是很酷。 即使你有其他的内存泄漏,你应该检查出来。

一般来说,你应该真的在视图中做什么是完全不同的,特别是视图已经有设施来处理定时和animation,而不需要线程。

我会build议你,我相信是一个更简单,更清洁的方法来animation的意见。

  • 首先删除您的可运行和完全线程。

然后开始你做的animation:

 ValueAnimator animation = ValueAnimator.ofFloat(0, 1); animation.setDuration(500); animation.addUpdateListener(animationUpdate); animation.addListener(animationUpdate); animation.start(); 

然后你需要这些听众

  // this gets called for every animation update, // inside this call you update `CircularProgressView.this.fadeAlpha` private final ValueAnimator.AnimatorUpdateListener animationUpdate = new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { // this fraction varies between 0f and 1f float fraction = animation.getAnimatedFraction(); // ... do your calculation ViewCompat.postInvalidateOnAnimation(CircularProgressView.this); } }; // this is an optional one only if you really need // in that you get notified when the animation starts and ends private final Animator.AnimatorListener animationListener = new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { // anything u need goes here ViewCompat.postInvalidateOnAnimation(CircularProgressView.this); } @Override public void onAnimationEnd(Animator animation) { // anything u need goes here ViewCompat.postInvalidateOnAnimation(CircularProgressView.this); } }; 

就是这个。

关于实际的内存泄漏分析的话题,我会build议你从现在开始永远使用漏洞库: https : //github.com/square/leakcanary这是帮助我们(开发者)跟踪内存泄漏的好工具。

编辑:

你为什么在这个animation中有内存泄漏? 这很简单:

  • startFade(boolean); 你创build一个新的线程和一个新的runnable
  • runnable有一个对视图的引用(因为它是一个非静态的内部类)
  • 该线程有Runnable的引用,所以可以运行它。
  • 框架破坏了视图,因为它不再是UI的一部分(旋转,后退button)
  • 线程仍在运行,可运行的仍然循环,View对象仍然不被销毁,因为Runnable引用它。
  • 视图对象有一个Context的实例,这个上下文是Activity

所以在这个序列的最后,你的活动将不会被垃圾收集,也就是说:内存泄漏!