Android StrictMode InstanceCountViolation
Asked Answered
C

6

55

I am running my app with StrictMode activated in development as documented here StrictMode for lower platform versions and noticed an error message that I do not know what to think about nor can I find any reference.

I get a android.os.StrictMode$InstanceCountViolation with values for instances and limit e.g.

instances=3; limit=2

Now I am wondering:

  • A) how is the limit calculated
  • B) how can such a violation actually happen and then I would look into evasive actions.

Any ideas?

Camp answered 10/5, 2011 at 20:34 Comment(3)
Ever figure this out? I've just run into it myself, for a preference activity.Opposite
I wish... at this stage I think it is a bug in the StrictMode because I was even able to verify that it is wrong using static variables and counting ..Camp
I get this violation when i navigate back from an activity to the parent activity. I have noticed that the frequency of this violation happening is considerably reduced when i use android:launchMode="singleTop" for the parent activity in the manifest.Fiber
M
31

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

Masaryk answered 28/12, 2014 at 18:38 Comment(1)
I am accepting this as answer since it provides all the detail necessary to hunt down the specific problem in your app. It still seems a rather odd logging but that would be up to the Android team to change.Camp
P
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.

Provost answered 10/3, 2014 at 16:51 Comment(0)
M
8

Here is a discussion on google groups about handling the StrictMode InstanceCountViolation. It looks like every different Android version has a different policy so they seem to just disable it. Also the Android docs say about Strict Mode

But don't feel compelled to fix everything that StrictMode finds. In particular, many cases of disk access are often necessary during the normal activity lifecycle. Use StrictMode to find things you did by accident. Network requests on the UI thread are almost always a problem, though.

I think that is what @sri is trying to show with his code.

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());
 }
}
Mcrae answered 24/1, 2013 at 9:19 Comment(1)
That all makes sense and I am aware of that. However it still does not really explain how instance counts are calculated and how that calculation is wrong or if I am doing something wrong..Camp
A
3

My understanding is that this violation is used to detect memory leaks. So at that point you should only have 2 instances of the class loaded, but the VM found 3.

I have seen this violation in my code also, but my extra instances were all referenced by weak pointers. So I choose to disable this rule.

Accordant answered 13/7, 2011 at 16:16 Comment(1)
In my case there should be only a single instance of the activity but it shows limit 2. So I am wondering how it gets the limit. Not to mention how 3 instances appear..Camp
S
-3

see the below example it varies based on android version

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());
    }
}
Seaman answered 22/1, 2013 at 11:50 Comment(1)
So what are you saying? How is this an answer?Camp
A
-3

Remove the line below from on create.

//StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().penaltyDeath().detectLeakedSqlLiteObjects().build());
Apprehensible answered 23/3, 2015 at 11:2 Comment(1)
That just removes reporting. It doesnt explain why the reporting is wrong though.Camp

© 2022 - 2024 — McMap. All rights reserved.