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