gsi*_*011 14 java multithreading android memory-leaks future
我想我的Android动态壁纸中有内存泄漏.每当我旋转屏幕时,收集的内存垃圾量增加50kb并且不会再次下降.我认为它可能是由预定的未来引起的,所以我将提出一个场景,看看是否是这种情况.
假设您有一个具有以下成员的类(我们称之为Foo).
private ScheduledFuture<?> future;
private final ScheduledExecutorService scheduler = Executors
.newSingleThreadScheduledExecutor();
private final Runnable runnable = new Runnable() {
public void run() {
// Do stuff
}
};
Run Code Online (Sandbox Code Playgroud)
现在你设定了预定的未来
future = scheduler.scheduleAtFixedRate(runnable, delay, speed,
TimeUnit.MILLISECONDS);
Run Code Online (Sandbox Code Playgroud)
将来会对runnable进行引用,而runnable可以保存对父Foo对象的引用.我不确定是不是这种情况,但这个事实是否意味着如果程序中没有任何内容引用Foo,垃圾收集器仍然无法收集它,因为有预定的未来?我不太擅长多线程,所以我不知道我显示的代码是否意味着计划任务的寿命比对象长,这意味着它不会被垃圾收集.
如果这种情况不会导致阻止Foo被垃圾收集,我只需要通过一个简单的解释告诉它.如果它确实阻止Foo被垃圾收集,那么我该如何解决?要做future.cancel(true); future = null;
什么?这future = null
部分是不必要的吗?
run
方法依赖于封闭Foo
类,因此无法独立生存.在那种情况下,我不知道你怎么能拥有你的Foo
gc'ed并让你的runnable"活着"由执行者运行run
方法是静态的,因为它不依赖于你的Foo
类的状态,在这种情况下你可以使它静态,它将防止你遇到的问题.您似乎没有处理Runnable的中断.这意味着即使你打电话给future.cancel(true)
你的Runnable也会继续运行,正如你所确定的那样可能是你泄漏的原因.
有几种方法可以使Runnable"中断友好".要么调用一个抛出InterruptedException的方法(比如Thread.sleep()
阻塞IO方法),它会InterruptedException
在取消未来时抛出.在清理了需要清理的内容并恢复中断状态后,您可以捕获该异常并立即退出run方法:
public void run() {
while(true) {
try {
someOperationThatCanBeInterrupted();
} catch (InterruptedException e) {
cleanup(); //close files, network connections etc.
Thread.currentThread().interrupt(); //restore interrupted status
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果你不调用任何这样的方法,标准的习惯用法是:
public void run() {
while(!Thread.currentThread().isInterrupted()) {
doYourStuff();
}
cleanup();
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,您应该尝试确保定期检查while中的条件.
通过这些更改,当您调用时future.cancel(true)
,中断信号将被发送到执行Runnable的线程,该线程将退出正在执行的操作,使您的Runnable和您的Foo实例符合GC的条件.
虽然这个问题很久以前就得到了解答,但在阅读完这篇文章之后,我想到了解释的新答案.
预定的未来会导致内存泄漏吗?---是的
ScheduledFuture.cancel()
或者Future.cancel()
通常不会通知Executor
它已被取消并且它一直停留在队列中,直到它的执行时间到来.这对于简单的期货来说并不是什么大问题,但对于简单的期货而言可能是个大问题ScheduledFutures
.它可以在那里停留数秒,数分钟,数小时,数天,数周,数年或几乎无限期,具体取决于计划的延迟.
以下是最糟糕情况的示例.即使在其Future已被取消之后,runnable及其引用的所有内容仍将保留在Queue for Long.MAX_VALUE毫秒内!
public static void main(String[] args) {
ScheduledThreadPoolExecutor executor
= new ScheduledThreadPoolExecutor(1);
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("Hello World!");
}
};
ScheduledFuture future
= executor.schedule(task,
Long.MAX_VALUE, TimeUnit.MILLISECONDS);
future.cancel(true);
}
Run Code Online (Sandbox Code Playgroud)
您可以通过使用Profiler
或调用ScheduledThreadPoolExecutor.shutdownNow()
方法来看到这一点,该方法将返回一个包含一个元素的List(它是取消的Runnable).
解决这个问题的方法是编写自己的Future实现或者不时地调用purge()
方法.如果是自定义Executor工厂解决方案是:
public static ScheduledThreadPoolExecutor createSingleScheduledExecutor() {
final ScheduledThreadPoolExecutor executor
= new ScheduledThreadPoolExecutor(1);
Runnable task = new Runnable() {
@Override
public void run() {
executor.purge();
}
};
executor.scheduleWithFixedDelay(task, 30L, 30L, TimeUnit.SECONDS);
return executor;
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
6940 次 |
最近记录: |