Java 在完全空活动的循环中,StrictMode活动实例计数冲突(2个实例,预期1个) 相关的唯一原因是它促使从严格模式中消除任何误报,因为任何错误的持续存在使得死刑不切实际
在过去的几天里,我一直在寻找并修复应用程序中的内存泄漏。到了我认为我已经解决了所有问题的时候,我实现了一个类似于中所述的故障响亮机制(使用死刑启用实例跟踪、截获关机错误消息、转储堆、下次应用程序启动时发送求救信号)。当然,所有只是调试版本 切中要害 相信我已经修复了所有活动泄漏,某些活动仍然会在屏幕旋转时发出严格的模式实例违反警告。奇怪的是,只有部分而不是所有应用程序的活动能够做到这一点 我已经检查了发生此类违规行为时采取的堆转储,并检查了搜索泄漏的相关活动的代码,但没有得到任何结果 所以在这一点上,我试着做尽可能小的测试用例。我创建了一个完全空白的活动(甚至没有布局),如下所示:Java 在完全空活动的循环中,StrictMode活动实例计数冲突(2个实例,预期1个) 相关的唯一原因是它促使从严格模式中消除任何误报,因为任何错误的持续存在使得死刑不切实际,java,android,memory-leaks,android-activity,Java,Android,Memory Leaks,Android Activity,在过去的几天里,我一直在寻找并修复应用程序中的内存泄漏。到了我认为我已经解决了所有问题的时候,我实现了一个类似于中所述的故障响亮机制(使用死刑启用实例跟踪、截获关机错误消息、转储堆、下次应用程序启动时发送求救信号)。当然,所有只是调试版本 切中要害 相信我已经修复了所有活动泄漏,某些活动仍然会在屏幕旋转时发出严格的模式实例违反警告。奇怪的是,只有部分而不是所有应用程序的活动能够做到这一点 我已经检查了发生此类违规行为时采取的堆转储,并检查了搜索泄漏的相关活动的代码,但没有得到任何结果 所以在这一
package com.example.app;
import android.app.Activity;
import android.os.Bundle;
import android.os.StrictMode;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
StrictMode.setVmPolicy(
new StrictMode.VmPolicy.Builder()
.detectAll()
.penaltyLog()
.build());
StrictMode.setThreadPolicy(
new StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyDeath()
.penaltyLog()
.build());
super.onCreate(savedInstanceState);
}
}
为完整起见,舱单:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app" >
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.app.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
充分披露
我只在一台设备上做过这些测试,一台运行CyanogenMod 11 snapshot M2()的Galaxy S4,但我无法想象CyanogenMod在活动管理方面会偏离KitKat
其他模式源:
活动的instanceTracker实例只是一个最终实例字段:
当预期的活动实例计数递增时:
当预期的活动实例计数递减时:
我不是Android开发者,所以这有点像是在黑暗中拍摄的
可能是因为其他活动的行为不一致,但却是由一个完全琐碎的活动触发的,所以问题取决于onCreate完成所需的时间
对于旧活动,如果onCreate可以在暂停/停止/销毁序列完成之前完成(以及最终的垃圾收集,尽管它不需要到达afaics的那个点),那么实际上将存在两个实例
防止这种情况的一个简单解决方案是让活动有一个静态的atomicboolean“ready”(最初为false),在onCreate中循环之前进行检查
while(!ready.compareAndSet(false, true)) {
//sleep a bit
}
然后覆盖ondestroy生命周期回调并调用ready.compareAndSet(true,false)
如果这种方法完全幼稚,我深表歉意。我同意door#2:这不是真正的泄密。
更具体地说,它只是与垃圾收集有关。三条线索:
到GC根的路径结束于,这与GC密切相关。它基本上处理对符合GC条件的对象调用finalize()
方法,这里有一个实例,即ViewRootImpl.WindowInputEventReceiver
实例,该实例扩展了InputEventReceiver
,它确实有一个finalize()
方法。如果这是一个“真实的”内存泄漏,那么该对象将不符合GC的条件,并且应该至少有一个到GC根的其他路径
至少在我的测试用例中,如果我在拍摄堆快照之前强制GC,那么只有一个对MainActivity
的引用(而如果我没有拍摄快照,则有两个引用)。看起来从DDMS强制GC实际上包括调用所有终结器(很可能是调用FinalizerReference.finalizeAllEnqueued()
,它应该释放所有这些引用
我可以在安卓4.4.4的设备上复制它,但不能在安卓L上复制,安卓L有一个新的GC算法(无可否认,这是最间接的证据,但它与其他的一致)
为什么有些活动会发生这种情况而其他活动不会发生这种情况?虽然我不能肯定,但很可能构造一个“更复杂”的活动会触发GC(只是因为它需要分配更多内存),而像这样的简单活动“通常”不会。但这应该是可变的
为什么StrictMode不这么想?
StrictMode
中有大量关于此案例的评论,请查看decrementExpectedActivityCount()
的源代码。不过,它看起来并没有完全按照预期工作
// Note: adding 1 here to give some breathing room during
// orientation changes. (shouldn't be necessary, though?)
limit = newExpected + 1;
...
// Quick check.
int actual = InstanceTracker.getInstanceCount(klass);
if (actual <= limit) {
return;
}
// Do a GC and explicit count to double-check.
// This is the work that we are trying to avoid by tracking the object instances
// explicity. Running an explicit GC can be expensive (80ms) and so can walking
// the heap to count instance (30ms). This extra work can make the system feel
// noticeably less responsive during orientation changes when activities are
// being restarted. Granted, it is only a problem when StrictMode is enabled
// but it is annoying.
Runtime.getRuntime().gc();
long instances = VMDebug.countInstancesOfClass(klass, false);
if (instances > limit) {
Throwable tr = new InstanceCountViolation(klass, instances, limit);
onVmPolicyViolation(tr.getMessage(), tr);
}
(注意:请确保在应用程序启动时只执行一次)。我想总结一下,并为其他开发人员提供安全时间的最终答案
android.os.StrictMode$InstanceCountViolation是否存在问题
这可能是一个真正的问题。要确定这是否是一个问题,我可以推荐以下帖子:。如果您看到有对象引用此活动,但与Android Framework无关,那么您有一个问题,应该由您解决
如果没有任何对象持有与Android框架无关的对此活动的引用,那么这意味着您遇到了与如何实现detectActivityLeaks检查相关的问题。正如所指出的,垃圾收集在这种情况下应该有所帮助
为什么detectActivityLeaks工作不正确,并且不能在所有设备上复制
如果我们看一下可检测的VityleAks的来源:
// Note: adding 1 here to give some breathing room during
// orientation changes. (shouldn't be necessary, though?)
limit = newExpected + 1;
...
// Quick check.
int actual = InstanceTracker.getInstanceCount(klass);
if (actual <= limit) {
return;
}
// Do a GC and explicit count to double-check.
// This is the work that we are trying to avoid by tracking the object instances
// explicity. Running an explicit GC can be expensive (80ms) and so can walking
// the heap to count instance (30ms). This extra work can make the system feel
// noticeably less responsive during orientation changes when activities are
// being restarted. Granted, it is only a problem when StrictMode is enabled
// but it is annoying.
Runtime.getRuntime().gc();
long instances = VMDebug.countInstancesOfClass(klass, false);
if (instances > limit) {
Throwable tr = new InstanceCountViolation(klass, instances, limit);
onVmPolicyViolation(tr.getMessage(), tr);
}
这也确保了版本内构建垃圾收集不会被强制,因为这是不推荐的。我在Nexus 7上观察到了相同的情况,库存为4.4.2 AndroidAlso,在Galaxy S3(i9300)上观察到了这一点,Cyanogenmod CM11 snapshot M7。请使用super.onCreate()作为onCreate()中的第一条语句运行测试为了倾听的人的利益。感谢Nexus 5和stock 4.4.4。AndroidI创建了一个带有主细节流的全新项目。Android Studio为该流提供了虚拟数据
// Note: adding 1 here to give some breathing room during
// orientation changes. (shouldn't be necessary, though?)
limit = newExpected + 1;
...
// Quick check.
int actual = InstanceTracker.getInstanceCount(klass);
if (actual <= limit) {
return;
}
// Do a GC and explicit count to double-check.
// This is the work that we are trying to avoid by tracking the object instances
// explicity. Running an explicit GC can be expensive (80ms) and so can walking
// the heap to count instance (30ms). This extra work can make the system feel
// noticeably less responsive during orientation changes when activities are
// being restarted. Granted, it is only a problem when StrictMode is enabled
// but it is annoying.
Runtime.getRuntime().gc();
long instances = VMDebug.countInstancesOfClass(klass, false);
if (instances > limit) {
Throwable tr = new InstanceCountViolation(klass, instances, limit);
onVmPolicyViolation(tr.getMessage(), tr);
}
if (BuildConfig.DEBUG)
{
System.gc();
}
Intent intent = new Intent(context, SomeActivity.class);
this.startActivity(intent);