【问题标题】:Constant Memory Leak in SpeechSynthesizerSpeechSynthesizer 中的持续内存泄漏
【发布时间】:2011-01-13 07:47:49
【问题描述】:

我开发了一个我想发布的项目,它使用 c#、WPF 和 System.Speech.Synthesizer 对象。阻止该项目发布的问题是,每当调用 SpeakAsync 时,它都会留下内存泄漏,并最终导致失败。我相信我在使用此物品后已正确清理,但找不到治疗方法。我已经通过 Ants Memory Profiler 运行了该程序,它报告 WAVEHDR 和 WaveHeader 在每次调用时都在增长。

我创建了一个示例项目来尝试查明原因,但仍然不知所措。任何帮助将不胜感激。

该项目使用 VS2008,是一个针对 .NET 3.5 和 Any CPU 的 c# WPF 项目。您需要手动添加对 System.Speech 的引用。

代码如下:

<Window x:Class="SpeechTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
    <StackPanel Orientation="Vertical">

        <Button Content="Start Speaking" Click="Start_Click" Margin="10" />
        <Button Content="Stop Speaking" Click="Stop_Click" Margin="10" />
        <Button Content="Exit" Click="Exit_Click" Margin="10"/>

    </StackPanel>
</Grid>



// Start of code behind
using System;
using System.Windows;
using System.Speech.Synthesis;

namespace SpeechTest
{
    public partial class Window1 : Window
    {

        // speak setting
        private bool speakingOn = false;
        private int curLine = 0;
        private string [] speakLines = {
            "I am wondering",
            "Why whenever Speech is called",
            "A memory leak occurs",
            "If you run this long enough",
            "It will eventually crash",
            "Any help would be appreciated" };

        public Window1()
        {
            InitializeComponent();
        }

        private void Start_Click(object sender, RoutedEventArgs e)
        {
            speakingOn = true;
            SpeakLine();
        }

        private void Stop_Click(object sender, RoutedEventArgs e)
        {
            speakingOn = false;
        }

        private void Exit_Click(object sender, RoutedEventArgs e)
        {
            App.Current.Shutdown();
        }

        private void SpeakLine()
        {
            if (speakingOn)
            {
                // Create our speak object
                SpeechSynthesizer spk = new SpeechSynthesizer();
                spk.SpeakCompleted += new EventHandler(spk_Completed);
                // Speak the line
                spk.SpeakAsync(speakLines[curLine]);
            }
        }

        public void spk_Completed(object sender, SpeakCompletedEventArgs e)
        {
            if (sender is SpeechSynthesizer)
            {

                // get access to our Speech object
                SpeechSynthesizer spk = (SpeechSynthesizer)sender;
                // Clean up after speaking (thinking the event handler is causing the memory leak)
                spk.SpeakCompleted -= new EventHandler(spk_Completed);
                // Dispose the speech object
                spk.Dispose();
                // bump it
                curLine++;
                // check validity
                if (curLine >= speakLines.Length)
                {
                    // back to the beginning
                    curLine = 0;
                }
                // Speak line
                SpeakLine();
            }
        }
    }
}




我在 Windows 7 64 位上运行此程序,它会在尝试创建新的 SpeechSynthesizer 对象时运行并最终停止。在 Windows Vista 64 位上运行时,内存将从起点的 34k 增长到目前的大约 400k 并且还在增长。

任何人都可以在代码中看到任何可能导致此问题的内容,或者这是 Speech 对象本身的问题。

任何帮助将不胜感激。

【问题讨论】:

  • 你确定它一直在上升吗? .net 中的内存将继续增加,直到 GC 通过并清理所有内容。除非它持续上升并且从不下降,否则我不会担心它。
  • 是的,当我在 Windows 7 上运行它时,它最终会在尝试创建新的 SpeechSynthesizer 对象时停止。程序停止后,转到控制面板并尝试测试文本转语音将得到相同的结果。在机器重新启动之前它不会再说话。
  • 如果您不在每次传递中创建新的 SpeechSynthesizer 对象会发生什么?
  • Eric,我第一次尝试过这种方式,但实际上更糟。这就是我尝试在每次调用时创建和销毁对象的原因。它确实略有改善,但仍然没有解决问题。任何对 SpeechSynthesizer 的调用都会留下 WAVEHDR 和 WaveHeader 对象,这些对象会增加私有内存直到崩溃。
  • 在不同的AppDomain 中调用语音是否有帮助,然后定期卸载该域,它会回收内存吗?

标签: c# wpf memory-leaks text-to-speech


【解决方案1】:

对于你的问题,我可以给你一个非常简单的答案: 将 SpeechSynthesizer 设为静态!!!

我很确定这会解决您的问题。

还有 - 提示 ==>> 每次您编写代码时,您都有资源...将其用作静态资源,您的生活会更好!

【讨论】:

  • 我不认为实例是线程安全的,你可能会遇到跨多个线程使用静态 SpeechSynthesizer 实例的问题。
【解决方案2】:

这是 Speak 方法中的一个已知问题。一个名为 SPVTEXTFRAG 的结构被创建并且永远不会被破坏。

详情在这里:http://connect.microsoft.com/VisualStudio/feedback/details/664196/system-speech-has-a-memory-leak

【讨论】:

  • 链接已失效
【解决方案3】:

来自PingSendAsync() 也会泄漏。解决方案是首先将发件人转换为IDisposable。所以也许以下也适用于此。

((IDisposable)spk).Dispose();

【讨论】:

    【解决方案4】:

    我可以证实这一观察。我正在拔头发,试图找出我的程序在哪里泄漏,它是 System.speech 中的 .SPEAK 方法

    我已将使用基于 COM 的语音对象的应用程序转换为使用 .Net 3.5 中的新 System.Speech .Net 库。听起来像是在 .Net 应用程序中使用所有托管代码的正确方法。该应用程序突然有一个小的内存泄漏。

    我将其分解为 2 个简单的应用程序,它们将“这是一个测试”转换为口语的 WAV 文件。一种使用基于 COM 的语音对象,另一种使用 System.Speech。我运行它们 24 小时,每个创建 WAV 大约 200,000 次。

    基于 COM 的语音对象:没有内存泄漏。大约 40 分钟后,应用的内存使用量达到 13MB 的峰值

    System.speech:缓慢的泄漏,漂亮和线性。在 24 小时内从大约 14MB 运行到 45MB

    【讨论】:

    • 我也遇到了泄漏,现在通过您的提示解决了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多