严格模式活动实例计数冲突(2 个实例,1 个预期)在轮换完全为空的活动时
相关之处仅在于它促使从严格模式中消除任何误报,因为任何误报的持续存在使得死刑不切实际
在过去的几天里,我一直在追逐并修复应用程序中的内存泄漏。在达到我相信我已经修复了所有问题的程度时,我实现了一个类似于Android StrictMode和堆转储中描述的故障响亮机制(启用具有死刑的实例跟踪,拦截关闭错误消息,转储堆,下次应用程序启动时发送求救信号)。当然,所有这些都只是在调试版本中。
切中要害
在相信我已经修复了所有活动泄漏之后,某些活动仍然会导致屏幕旋转时出现严格的模式实例违规警告。奇怪的是,只有一些,而不是所有应用程序的活动来做到这一点。
我已经审查了发生此类违规行为时采取的堆转储,并审查了相关活动的代码以查找泄漏,但没有得到任何结果。
因此,在这一点上,我试图制作尽可能小的测试用例。我创建了一个完全空白的活动(甚至没有布局),看起来像这样:
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>
我打开活动(设备保持肖像)。我旋转到横向,然后回到纵向,此时我从LogCat中的StrictMode中看到:
01-15 17:24:23.248: E/StrictMode(13867): class com.example.app.MainActivity;实例数 = 2;limit=1 01-15 17:24:23.248: E/StrictMode(13867): android.os.StrictMode$InstanceCountViolation: class com.example.app.MainActivity;实例数 = 2;limit=1 01-15 17:24:23.248: E/StrictMode(13867): at android.os.StrictMode.setClassInstanceLimit(StrictMode.java:1)
堆转储
此时,我使用 DDMS 手动获取堆转储。以下是 MAT 中的两个实例:
这是泄露的那个,从它到GC根的路径的第一部分:
请注意,无论我在纵向和横向之间旋转多少次,实际实例数永远不会超过 2,并且预期实例数永远不会超过 1。
任何人都可以理解泄漏吗?这甚至是真正的泄漏吗?如果是,则可能一定是Android错误。如果不是,我能看到的唯一另一件事可能是StrictMode中的一个错误。
好的答案可能包括
- 如果这是一个真正的泄漏,解释它是如何发生的,以及是否可以采取任何行动来修复它或禁止StrictMode对此类案件启动死刑(回想一下,误报/中性使死刑不切实际)。
- 如果这不是一个真正的泄漏,解释为什么StrictMode不这么认为,以及如果可以采取任何行动来修复它或禁止StrictMode对此类案件启动死刑(回想一下,误报/中性使死刑不切实际)。
- 在任何一种情况下,假设为什么所有活动都不会发生这种情况(我正在处理的应用程序中的大多数活动不会在轮换时产生此类冲突)。
到目前为止,我已经查看了StrictMode源代码,并且没有看到任何明显的错误 - 正如预期的那样,在考虑将额外的实例视为违规之前,它会强制GC。
阅读StrictMode源代码的良好切入点是:
- http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4_r1/android/os/StrictMode.java#StrictMode.trackActivity%28java.lang.Object%29
- http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4_r1/android/os/StrictMode.java#StrictMode.InstanceTracker.finalize%28%29
- http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4_r1/android/os/StrictMode.java#StrictMode.incrementExpectedActivityCount%28java.lang.Class%29
- http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4_r1/android/os/StrictMode.java#StrictMode.decrementExpectedActivityCount(java.lang.Class)
全面披露
我只在一台设备上进行了这些测试,即运行CyanogenMod 11快照M2的Galaxy S4(http://download.cyanogenmod.org/get/jenkins/53833/cm-11-20140104-SNAPSHOT-M2-jfltexx.zip),但我无法想象CyanogenMod会在活动管理方面偏离KitKat。
其他严格模式来源:
- 活动的实例跟踪器实例只是一个最终实例字段:http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4_r1/android/app/Activity.java#Activity.0mInstanceTracker
- 预期活动实例计数递增的位置:http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4_r1/android/app/ActivityThread.java#2095
- 减少预期活动实例计数的位置:http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4_r1/android/app/ActivityThread.java#3485