Séb*_*ien 313 java android memory-leaks inner-classes
我一直在阅读一些有关Android内存泄漏的文章,并观看了Google I/O 关于此主题的有趣视频.
尽管如此,我还是不完全理解这个概念,特别是当在Activity中使用内部类是安全或危险的时候.
这就是我的理解:
如果内部类的实例比其外部类(活动)存活的时间更长,则会发生内存泄漏.- > 在哪种情况下会发生这种情况?
在这个例子中,我认为没有泄漏的风险,因为匿名类扩展没有办法OnClickListener
比活动更长寿,对吧?
final Dialog dialog = new Dialog(this);
dialog.setContentView(R.layout.dialog_generic);
Button okButton = (Button) dialog.findViewById(R.id.dialog_button_ok);
TextView titleTv = (TextView) dialog.findViewById(R.id.dialog_generic_title);
// *** Handle button click
okButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
dialog.dismiss();
}
});
titleTv.setText("dialog title");
dialog.show();
Run Code Online (Sandbox Code Playgroud)
现在,这个例子是危险的,为什么?
// We are still inside an Activity
_handlerToDelayDroidMove = new Handler();
_handlerToDelayDroidMove.postDelayed(_droidPlayRunnable, 10000);
private Runnable _droidPlayRunnable = new Runnable() {
public void run() {
_someFieldOfTheActivity.performLongCalculation();
}
};
Run Code Online (Sandbox Code Playgroud)
我对于理解这个主题与详细了解活动被销毁和重新创建时所保留的内容有关.
是吗?
假设我刚改变了设备的方向(这是泄漏的最常见原因).什么时候super.onCreate(savedInstanceState)
会在我的中调用,这onCreate()
会恢复字段的值(就像在方向改变之前一样)吗?这还会恢复内部阶级的状态吗?
我意识到我的问题不是很精确,但我真的很感激任何可以使事情变得清晰的解释.
Fuz*_*gic 634
你问的是一个非常棘手的问题.虽然您可能认为这只是一个问题,但实际上您实际上是在问几个问题.我会尽我所能来覆盖它,并且希望其他一些人能够加入到我可能想念的范围内.
嵌套类:简介
由于我不确定你在Java中使用OOP有多舒服,这将有几个基础.嵌套类是在另一个类中包含类定义的时候.基本上有两种类型:静态嵌套类和内部类.这些之间的真正区别是:
垃圾收集和内部类
垃圾收集是自动的,但会尝试根据对象是否认为正在使用它们来删除它们.垃圾收集器非常聪明,但并不完美.它只能通过是否存在对象的活动引用来确定是否正在使用某些内容.
这里真正的问题是内部类比它的容器保持活着的时间长.这是因为对包含类的隐式引用.发生这种情况的唯一方法是,如果包含类之外的对象保留对内部对象的引用,而不考虑包含对象.
这可能导致内部对象处于活动状态(通过引用),但是已经从所有其他对象中删除了对包含对象的引用.因此,内部对象使包含对象保持活动状态,因为它始终具有对它的引用.这个问题是,除非它被编程,否则无法返回到包含对象以检查它是否还活着.
这种认识最重要的方面是,无论是在活动中还是在绘制中都没有区别.在使用内部类时,您将始终必须是有条理的,并确保它们永远不会超过容器的对象.幸运的是,如果它不是代码的核心对象,相比之下泄漏可能很小.不幸的是,这些是最难发现的漏洞,因为它们可能会被忽视,直到它们中的许多漏洞.
解决方案:内部课程
活动和观点:简介
活动包含许多能够运行和显示的信息.活动由他们必须拥有View的特征定义.他们还有一些自动处理程序.无论您是否指定它,Activity都会隐式引用它包含的View.
为了创建视图,它必须知道创建它的位置以及它是否有任何子节点以便它可以显示.这意味着每个View都有对Activity(via getContext()
)的引用.此外,每个视图都会保留对其子项的引用(即getChildAt()
).最后,每个View都会保留对表示其显示的渲染位图的引用.
每当您引用Activity(或Activity Context)时,这意味着您可以沿着布局层次结构沿着整个链.这就是为什么有关活动或视图的内存泄漏是如此巨大的原因.一下子就可以泄露大量的内存.
活动,观点和内在阶级
鉴于上面有关内部类的信息,这些是最常见的内存泄漏,但也是最常见的内存泄漏.虽然希望内部类可以直接访问Activities类成员,但是许多人愿意将它们设置为静态以避免潜在的问题.活动和观点的问题比这更深刻.
泄露的活动,观点和活动背景
这一切都归结为Context和LifeCycle.某些事件(例如方向)会杀死活动上下文.由于许多类和方法需要Context,因此开发人员有时会尝试通过获取对Context的引用并保留它来保存一些代码.碰巧的是,我们必须创建的许多用于运行Activity的对象必须存在于Activity LifeCycle之外,以便允许Activity执行它需要做的事情.如果你的任何对象碰巧有一个对Activity,它的Context或它的任何Views被销毁的引用,你刚刚泄露了该Activity及其整个View树.
解决方案:活动和观点
getBaseContext()
或getApplicationContext()
).这些不会隐含地保留引用.Runnables:简介
Runnables实际上并没有那么糟糕.我的意思是,它们可能是,但实际上我们已经击中了大部分危险区域.Runnable是一个异步操作,它执行与创建它的线程无关的任务.大多数runnable都是从UI线程实例化的.从本质上讲,使用Runnable创建另一个线程,只是稍微管理一下.如果您将Runnable归类为标准类并遵循上面的指导原则,那么您应该遇到一些问题.现实是许多开发人员不这样做.
由于易用性,可读性和逻辑程序流,许多开发人员使用匿名内部类来定义他们的Runnables,例如您在上面创建的示例.这会产生一个类似上面输入的示例.匿名内部类基本上是一个离散的内部类.您只需创建一个全新的定义并简单地覆盖适当的方法即可.在所有其他方面,它是一个内部类,这意味着它保持对其容器的隐式引用.
Runnables和活动/视图
好极了!这部分可以简短!由于Runnables在当前线程之外运行,因此这些问题的危险在于长时间运行的异步操作.如果runnable在Activity或View中定义为匿名内部类或嵌套内部类,则存在一些非常严重的危险.这是因为,如前所述,它必须知道其容器是谁.输入方向更改(或系统终止).现在回顾前面几节,了解刚刚发生的事情.是的,你的例子非常危险.
解决方案:Runnables
回答最终问题 现在回答本文其他部分没有直接解决的问题.你问过"内部阶级的对象什么时候能够比外部阶级存活更长时间?" 在我们谈到这一点之前,让我再强调一下:虽然你在活动中担心这一点是正确的,但它可能导致任何地方的泄漏.我将提供一个简单的例子(不使用Activity)来演示.
下面是基本工厂的常见示例(缺少代码).
public class LeakFactory
{//Just so that we have some data to leak
int myID = 0;
// Necessary because our Leak class is an Inner class
public Leak createLeak()
{
return new Leak();
}
// Mass Manufactured Leak class
public class Leak
{//Again for a little data.
int size = 1;
}
}
Run Code Online (Sandbox Code Playgroud)
这不是一个常见的例子,但很容易证明.这里的关键是构造函数......
public class SwissCheese
{//Can't have swiss cheese without some holes
public Leak[] myHoles;
public SwissCheese()
{//Gotta have a Factory to make my holes
LeakFactory _holeDriller = new LeakFactory()
// Now, let's get the holes and store them.
myHoles = new Leak[1000];
for (int i = 0; i++; i<1000)
{//Store them in the class member
myHoles[i] = _holeDriller.createLeak();
}
// Yay! We're done!
// Buh-bye LeakFactory. I don't need you anymore...
}
}
Run Code Online (Sandbox Code Playgroud)
现在,我们有泄漏,但没有工厂.即使我们发布了Factory,它也会保留在内存中,因为每个Leak都会引用它.外层没有数据也没关系.这种情况比人们想象的要频繁得多.我们不需要创作者,只需要创作.所以我们暂时创建一个,但无限期地使用创作.
想象一下当我们稍微改变构造函数时会发生什么.
public class SwissCheese
{//Can't have swiss cheese without some holes
public Leak[] myHoles;
public SwissCheese()
{//Now, let's get the holes and store them.
myHoles = new Leak[1000];
for (int i = 0; i++; i<1000)
{//WOW! I don't even have to create a Factory...
// This is SOOOO much prettier....
myHoles[i] = new LeakFactory().createLeak();
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在,这些新的LeakFactories中的每一个都刚刚被泄露.你对那个怎么想的?这是两个非常常见的例子,说明内部类如何能够比任何类型的外部类寿命更长.如果那个外类是一个活动,想象它本来会有多糟糕.
结论
这些列出了不恰当地使用这些对象的主要已知危险.一般来说,这篇文章应该涵盖了你的大部分问题,但是我知道这是一个很好的帖子,所以如果你需要澄清,请告诉我.只要您遵循上述做法,您就不必担心泄漏.
归档时间: |
|
查看次数: |
51135 次 |
最近记录: |