Java 匿名类构造函数中对最终对象的引用是否泄漏?
我正在将runnable传递给服务。可运行的持续时间可能超过传递它的片段/活动。我想知道下面的代码片段是否会通过在runnable中维护引用而泄漏frag对象?我意识到我可以将包含frag的整行代码移到runnable之外(就像我对“String key=frag…”所做的那样),但我只是想了解匿名类如何/何时泄漏对象 我真的不确定。runnable只需要frag在初始化实例时确定一个变量,它在这里是在线初始化的。因此,从理论上讲,在创建内嵌实例之后,它不需要对frag进行任何引用 如果我在run()函数中引用了frag,我认为泄漏是可以保证的,因为它需要让frag保持活动状态,以便在将来某个时候引用它(此时frag很可能是gc'd,但用于引用)Java 匿名类构造函数中对最终对象的引用是否泄漏?,java,android,memory-leaks,anonymous-class,Java,Android,Memory Leaks,Anonymous Class,我正在将runnable传递给服务。可运行的持续时间可能超过传递它的片段/活动。我想知道下面的代码片段是否会通过在runnable中维护引用而泄漏frag对象?我意识到我可以将包含frag的整行代码移到runnable之外(就像我对“String key=frag…”所做的那样),但我只是想了解匿名类如何/何时泄漏对象 我真的不确定。runnable只需要frag在初始化实例时确定一个变量,它在这里是在线初始化的。因此,从理论上讲,在创建内嵌实例之后,它不需要对frag进行任何引用 如果我在ru
我不确定我是否正确理解您使用的单词inline,但无论如何,我不认为这里有任何泄漏 Var
final String key
只是静态函数fg_downloadFile()
中的一个本地引用,因此在函数末尾超出范围时会释放它
Runnable
匿名内部类在Runnable
线程有效期内可能仍然持有引用(同样,不是指向frag
,而是指向其中的某个子属性)。(更准确地说,编译器实际上生成了可运行的的实现
,其中包含一个构造函数和final
成员变量,这些变量将所有隐式使用的引用复制到fg_downloadFile()
范围内的任何可用引用。)
但在任何情况下,一旦执行了all代码,对frag的(子成员)的所有引用都将被删除,垃圾收集器可以捡起垃圾
希望这有帮助。有趣的问题。你有没有在没有自动垃圾收集的情况下编程的经验,比如Objective C
但要回答你的问题,我不明白为什么会有泄漏。
我认为会发生的事情是Runnable将保留对frag的引用以供自己使用,但在将来的某个时候,一旦服务不再需要它,frag也将被垃圾收集,因此它不再引用它
随着垃圾收集的工作,一旦对象不再被引用,它将接受垃圾收集和释放 如您所知,在匿名类中使用变量时,在外部声明变量时,必须使变量为final。这里的Java技巧是将所有这些变量复制到该匿名类的隐式生成的实例字段中
话虽如此,这意味着确实有实例字段(在您的runnable中)保存外部作用域的所有访问变量的副本。在您的示例中,它还将引用FragMyDrive
,因为您只是在访问它
所有这些对象都有资格进行垃圾收集,同时您的runnable也有资格进行垃圾收集。这意味着在runnable中引用FragMyDrive
,可以使该对象在运行期间保持活动状态
缩小你真正需要的参考范围总是一个好主意:
private static void fg_downloadFile(final FragMyDrive frag, final File file, final Drive drive){
final String key = frag.getCastedActivity().getKey();
final Context context = frag.getCastedActivity().mService;
Runnable rx_downloadFile = new Runnable() {
@Override
public void run() {
bg_downloadFile(context, key, file, drive);
}
};
//.... Submit runnable to service...
}
此处唯一(隐式生成的)实例字段为:
String key
Context context
File file
Drive drive
我意识到(几乎)gc语言中的所有内存泄漏都不太符合正常内存泄漏的定义。这里我担心的是,在文件下载之前,服务会一直保持片段的活动状态……任务可能会在队列中等待一段时间,例如,等待启用wifi。我猜术语“正常内存泄漏”与您使用的编程环境有关。是的,碎片肯定会有一段时间是活的。还有其他一些因素导致这种不受欢迎的大内存使用等吗?我知道frag不会在“key=frag”行中泄漏,我想知道的是serviceContext=frag行,它在runnable中。我们是否都同意,如果我在runnable的run()函数中引用frag,那么肯定是泄漏?如果构造函数被视为任何旧函数,那么通过类比,它会泄漏…我们必须假设构造函数不是任何旧函数,编译器非常聪明,知道匿名函数的构造函数只需要一次,这样它就可以在初始化实例后丢弃frag引用。我同意Runnable
中的引用将在fg\u downloadFile()
完成后保留,但这不是泄漏。正如下面@seelenvirtuose所表示的,一旦Runnable
被清理,GC最终将清理所有对象。在Runnable
“instance”中包含隐式成员的全部目的是确保引用对象的活动时间与run()
函数中发生的任何事件一致。这和C++中调用的内容很接近。我意识到,你依赖的是内存泄漏的定义,java中没有任何东西是泄漏的。我关心的实际上是服务使片段保持活动的时间,因为任务可能无限期地位于服务中(例如等待wifi)。在java/android中,leak经常被用来指代“保留引用从而阻止GC收集它”。在这种情况下,问题不在于java或runnable中的引用,而在于(池)引用的生存期。在任何情况下,如果您的run函数不需要(隐式)传递到runnable中的所有信息,只需确保它没有被传递。不要给你不需要的东西。无论如何,我看不出有没有final
和它有什么关系
String key
Context context
File file
Drive drive