Android StrictMode InstanceCountViolation

我正在运行我的应用程序与StrictMode在开发中激活,这里logging的StrictMode为较低的平台版本,并注意到一个错误消息,我不知道该怎么想,也不能find任何参考。

我得到一个android.os.StrictMode$InstanceCountViolation的值和limit例如

实例= 3; 极限= 2

现在我想知道:

  • A)如何计算极限
  • B)怎样才能真正发生这样的违规行为,然后我会考虑回避行动。

有任何想法吗?

这些都在代码中

关键是StrictMode.sExpectedActivityInstanceCountincrementExpectedActivityCountdecrementExpectedActivityCount

  • ActivityThread.performLaunchActivity调用Increment
    只是在创buildActivity实例之后。
  • ActivityThread.performDestroyActivity调用减量
    活动已从应用程序中删除后。

所以每当一个活动被破坏的时候,这个limit就会减less,但是如果一个实例被泄露了,真实的实例数将会大于这个限制,为了检测它是否被泄漏了,他们会执行一些GC魔术(在decrementExpectedActivityCount ):

  System.gc(); System.runFinalization(); // added in https://github.com/android/platform_frameworks_base/commit/6f3a38f3afd79ed6dddcef5c83cb442d6749e2ff System.gc(); 

如果在此之后GC没有从应用程序的内存中删除该活动,则认为是泄漏。

结论

基于上面的唯一方法是确保在onDestroy之后没有对违规活动的引用。 问题是可能有一些WeakReference可以通过一些本地对象访问,这些对象似乎有不同的生命周期。 下面是我得出这个结论的方法:

  • 退出MyActivity并看到日志消息后
  • 做一个堆转储(.hprof)
  • Eclipse Memory Analyzer中打开它
  • 运行OQL: select * from instanceof full.package.name.of.MyActivity
  • Ctrl +单击Shift +单击select全部
  • 右键单击并合并到GC根的最短path>与所有引用

解决方法

如果我们最初增加计数 ,那么在报告特定类别的泄漏之前,我们会有更多的空间:

 // Application.onCreate or nearby where you set up StrictMode detectActivityLeaks Method incrementExpectedActivityCount = StrictMode.class.getMethod("incrementExpectedActivityCount", Class.class) incrementExpectedActivityCount.invoke(null, MyActivity.class); incrementExpectedActivityCount.invoke(null, MyActivity2.class); 

进一步阅读

  • WeakReference s
  • Eclipse内存分析器(MAT)
  • StrictMode历史

看来有些设备上的StrictMode检查可能存在一个错误。

如果一个Activity被启动,并且很快退出并重新启动,你可以得到一个StrictMode.InstanceCountViolation。

但是,这只是因为垃圾收集器尚未完成Activity的第一个实例,这意味着内存中暂时存在2个(或更多)实例。

在startActivity()或startActivityForResult()之前调用System.gc()将停止StrictMode.InstanceCountViolation。

这似乎表明在StrictMode检查中的一个错误(或者一个function?)。

以下是关于处理StrictMode InstanceCountViolation的谷歌群组的讨论。 它看起来像每个不同的Android版本有不同的政策,所以他们似乎只是禁用它。 Android文档也提到严格模式

但是不要强迫所有StrictMode发现。 特别是,在正常的活动生命周期中,通常需要很多磁盘访问的情况。 使用StrictMode来查找你意外做的事情。 但是,UI线程上的networking请求几乎总是一个问题。

我认为这就是@sri试图用他的代码展示的东西。

 public class MyApplication extends Application { @Override public void onCreate (){ super.onCreate(); // when you create a new application you can set the Thread and VM Policy StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectCustomSlowCalls() // API level 11, to use with StrictMode.noteSlowCode .detectDiskReads() .detectDiskWrites() .detectNetwork() .penaltyLog() .penaltyFlashScreen() // API level 11 .build()); //If you use StrictMode you might as well define a VM policy too StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() .detectLeakedSqlLiteObjects() .detectLeakedClosableObjects() // API level 11 .setClassInstanceLimit(Class.forName(“com.apress.proandroid.SomeClass”), 100) .penaltyLog() .build()); } } 

我的理解是,这种违规行为是用来检测内存泄漏。 所以在这一点上,你应该只加载了2个类的实例,但find了3个虚拟机。

我在我的代码中也看到了这个违例,但是我的额外实例都被弱指针引用。 所以我select禁用这个规则。

从创build时删除下面的行。

 //StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().penaltyDeath().detectLeakedSqlLiteObjects().build()); 

看下面的例子,它根据Android版本而变化

 public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectCustomSlowCalls() // API level 11, to use with StrictMode.noteSlowCode .detectDiskReads() .detectDiskWrites() .detectNetwork() .penaltyLog() .penaltyFlashScreen() // API level 11 .build()); // not really performance-related, but if you use StrictMode you might as well define a VM policy too StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() .detectLeakedSqlLiteObjects() .detectLeakedClosableObjects() // API level 11 .setClassInstanceLimit(Class.forName(“com.apress.proandroid.SomeClass”), 100) // API level 11 .penaltyLog() .build()); } }