【问题标题】:TextToSpeech and memory leakTextToSpeech 和内存泄漏
【发布时间】:2013-11-08 07:30:45
【问题描述】:

由于内存不足(在程序中,而不是程序员中),我的应用程序崩溃了。 MAT 显示,我的 Activity 的副本有时会在屏幕旋转时保留,而唯一使虚假副本保持活动状态的对象是每个实例的 TextToSpeech 对象。我可以使用这个 sn-p 复制这种行为:

public class MainActivity extends Activity {
    TextToSpeech    mTts;
    char[]          mBigChunk = new char[1000000];  // not used; just makes MainActivity instances easier to see in MAT

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    @Override
    public void onStart() {
        super.onStart();
        if (mTts==null)                             // shouldn't be necessary and doesn't make any difference
            mTts = new TextToSpeech(this, null);        // commenting this out fixes the leak
    }
    @Override
    public void onStop() {
        super.onStop();
        if (mTts != null) {
            mTts.shutdown();
            mTts = null;        // shouldn't be necessary and doesn't make any difference
        }
    }
}

在 30 次方向更改后,MAT 列出了 1 到 8 个 net.catplace.tts_leak.MainActivity 实例,以及各种 TTS 对象的多个实例;例如:

Class Name                                                            | Shallow Heap | Retained Heap | Percentage
------------------------------------------------------------------------------------------------------------------
android.speech.*                                                      |              |               |           
android.speech.tts.TextToSpeech$Connection$1 @ 0x42de94c8 Native Stack|           24 |     2,052,664 |     11.85%
android.speech.tts.TextToSpeech$Connection$1 @ 0x431dd500 Native Stack|           24 |     2,052,664 |     11.85%
android.speech.tts.TextToSpeech$Connection$1 @ 0x435cc438 Native Stack|           24 |           552 |      0.00%
android.speech.tts.TextToSpeech$Connection @ 0x441b3698               |           32 |           528 |      0.00%
android.speech.tts.TextToSpeech @ 0x43fb3c00                          |           64 |           496 |      0.00%
android.speech.tts.TextToSpeech$Connection @ 0x43fb4420               |           32 |            48 |      0.00%
android.speech.tts.TextToSpeech$Connection$1 @ 0x43fb4440 Native Stack|           24 |            24 |      0.00%
android.speech.tts.TextToSpeech$Connection$1 @ 0x441b36b8 Native Stack|           24 |            24 |      0.00%
Total: 8 entries (13,079 filtered)                                    |              |               |           
------------------------------------------------------------------------------------------------------------------

MAT 表示 MainActivity 的伪造副本正在被 TTS 保留:

Class Name                                                                            | Shallow Heap | Retained Heap
---------------------------------------------------------------------------------------------------------------------
                                                                                      |              |              
net.catplace.tts_leak.MainActivity @ 0x437c6068                                       |          200 |     2,001,352
'- mContext android.speech.tts.TextToSpeech @ 0x431de6d8                              |           64 |           496
   '- this$0 android.speech.tts.TextToSpeech$Connection @ 0x441b3698                  |           32 |           528
      '- this$1 android.speech.tts.TextToSpeech$Connection$1 @ 0x441b36b8 Native Stack|           24 |            24
---------------------------------------------------------------------------------------------------------------------

我在一系列真实设备和 AVD 中都得到了这种行为。以上结果来自 Nexus 7。

我尝试过不同的 TTS 引擎,使用不同的事件来创建和销毁 mTts 等。

我的假设是 TextToSpeech 并不总是将其对创建它的上下文的引用归零,从而导致上下文(活动)的泄漏副本。但我对此并不陌生;是不是我做错了什么?

【问题讨论】:

  • 如果 TextToSpeech 对象是通过传递 getApplicationContext() 而不是 this (Activity) 构造的,则问题似乎不会发生。这有点道理,因为应用程序不会在方向更改时被破坏。不过,我认为没有必要这样做。
  • 我遇到了同样的问题。屏幕旋转导致我的活动被泄露。旋转 10 次,在内存中复制 10 个活动。正如彼得建议的那样,我将其更改为将 getApplicationContext() 而不是这个传递给 TextToSpeech 对象,它解决了问题。轰隆隆!

标签: android memory-leaks text-to-speech google-text-to-speech


【解决方案1】:

如果没有实现android:configChanges="orientation",那么你可以覆盖onDestory方法:

@Override
protected void onDestroy() {
    super.onDestroy();
    if (ttsEngine != null) {
        ttsEngine.stop();
        ttsEngine.shutdown();
        Log.d(TAG, "TTS destroyed");
    }
}

【讨论】:

  • 试过了。它仍然会泄漏(有时)。
【解决方案2】:

查看 TextToSpeech here 的源代码,您会注意到它实际上将服务绑定到传递的上下文,而关闭方法实际上取消绑定它。现在休息是猜测,因为服务有自己的生命周期,TextToSpeech 可能会阻碍上下文。如果你做了一些研究,记住你实际上是在运行服务,你可能会破解这个问题。

现在我也不确定这可能意味着什么,但我愿意接受您方面的任何新发现。 鉴于 TextToSpeech 是一项服务,您可能希望将应用程序上下文传递给它,因为当活动被销毁时,该服务仍将运行。

Also for further reading ______________

【讨论】:

  • 再次感谢。我确实注意到传递应用程序上下文似乎使问题消失了。很遗憾 Google 的示例使用了 Activity 上下文。您的第二个链接显示该服务试图做聪明(和异步)的事情,这可以解释我注意到的不一致的内存泄漏行为。这可能完全取决于方向变化发生的速度。
  • stackoverflow.com/questions/7298731/…“commonsware”的答案,虽然与讨论的主题没有直接关系,但它很好地了解了何时使用什么上下文。
  • 很棒的发现。我认为它可能直接相关,因为它说要使用应用程序上下文来提供服务(正如您之前指出的那样,TTS 就是这样)。谷歌的相反例子似乎是在鼓励泄密。
  • 谢谢,只有一个问题,如果您通过应用程序上下文,问题会消失吗?从某种意义上说,MAT 是否显示任何剩余实例?
  • 越来越奇怪了。当我传递应用程序上下文时, android.speech.tts.TextToSpeech$Connection$1 的多个实例仍然保留(不是每次旋转,但可能是 1/3)。这些不会占用太多内存,因为它们不持有 Activity 的引用,并且不会产生 Activity 的多个副本。但是,当通过 Activity 上下文创建 TTS 时,android.speech.tts.TextToSpeech$Connection$1 的实例以大致相同的速率堆积,但要大得多,因为每个实例都包含一个 Activity 实例。 MAT 还显示多个 Activity 实例 - 但不是每个 TTS 一个!
猜你喜欢
  • 2010-12-02
  • 2016-05-03
  • 2011-11-25
  • 2010-11-19
  • 2014-09-27
  • 2013-07-31
  • 1970-01-01
  • 2011-04-03
  • 1970-01-01
相关资源
最近更新 更多