53

我正在使用开发中激活的 StrictMode 运行我的应用程序,如此处记录的StrictMode 用于较低平台版本 ,并注意到一条错误消息,我不知道要考虑什么,也找不到任何参考。

我得到了一个android.os.StrictMode$InstanceCountViolationinstanceslimit例如

实例=3;限制=2

现在我想知道:

  • A) 限额是如何计算的
  • B)这种违规行为如何实际发生,然后我会调查规避行为。

有任何想法吗?

4

6 回答 6

29

It's all in the code

The key is StrictMode.sExpectedActivityInstanceCount and incrementExpectedActivityCount and decrementExpectedActivityCount:

So the limit is less each time an activity is destroyed, however if an instance is leaked the real instance count will be bigger than the limit, to detect if it's leaked they do some GC magic (in decrementExpectedActivityCount):

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

if after this the GC didn't remove the activity from the app's memory it is considered a leak.

Conclusion

Based on the above the only way to prevent is to make sure there are no references to the offending activity after onDestroy. The problem is that some there may be some WeakReferences which are still accessible through some native objects, which seem to have a different lifecycle. Here's how I came to this conclusion:

  • after backing out from MyActivity and seeing the log message
  • make a heap dump (.hprof)
  • open it in Eclipse Memory Analyzer
  • run OQL: select * from instanceof full.package.name.of.MyActivity
  • select all with Ctrl+Click or Shift+Click
  • right click and Merge Shortest Path to GC Roots > with all references

Workaround

If we increase the count initially we'll have more legroom before it reports the leak for specific classes:

// 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);

Further reading

于 2014-12-28T18:38:12.890 回答
10

It seems there might be a bug in the StrictMode checking on some devices.

If an Activity is started, and exited and restarted very quickly, you can get a StrictMode.InstanceCountViolation.

However this is simply because the garbage collector has not yet finalized the first instance of the Activity, meaning there are temporarily 2 (or more) instances in memory.

Calling System.gc() before startActivity() or startActivityForResult() will stop the StrictMode.InstanceCountViolation.

This seems to indicate a bug (or perhaps a feature?) in the StrictMode checking.

于 2014-03-10T16:51:01.167 回答
8

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

但不要觉得有必要修复 StrictMode 找到的所有内容。特别是,在正常的活动生命周期中,许多磁盘访问情况通常是必要的。使用 StrictMode 查找您不小心做的事情。不过,UI 线程上的网络请求几乎总是一个问题。

我认为这就是@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());
 }
}
于 2013-01-24T09:19:01.123 回答
3

我的理解是这个违规是用来检测内存泄漏的。因此,此时您应该只加载了 2 个该类的实例,但 VM 找到了 3 个。

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

于 2011-07-13T16:16:04.520 回答
-3

请参阅下面的示例,它根据 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());
    }
}
于 2013-01-22T11:50:08.290 回答
-3

Remove the line below from on create.

//StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().penaltyDeath().detectLeakedSqlLiteObjects().build());
于 2015-03-23T11:02:19.800 回答