Constant Memory Leak in SpeechSynthesizer

2019-04-06 09:55发布

I have developed a project which I would like to release which uses c#, WPF and the System.Speech.Synthesizer object. The issue preventing the release of this project is that whenever SpeakAsync is called it leaves a memory leak that grows to the point of eventual failure. I believe I have cleaned up properly after using this object, but cannot find a cure. I have run the program through Ants Memory Profiler and it reports that WAVEHDR and WaveHeader is growing with each call.

I have created a sample project to try to pinpoint the cause, but am still at a loss. Any help would be appreciated.

The project uses VS2008 and is a c# WPF project that targets .NET 3.5 and Any CPU. You need to manually add a reference to System.Speech.

Here is the Code:

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




I run this program on Windows 7 64 bit and it will run and eventually halt when attempting to create a new SpeechSynthesizer object. When run on Windows Vista 64 bit the memory will grow from a starting point of 34k to so far about 400k and growing.

Can anyone see anything in the code that might be causing this, or is this an issue with the Speech object itself.

Any help would be appreciated.

4条回答
混吃等死
2楼-- · 2019-04-06 10:25

I can confirm this observation. I was pulling my hair out trying fo figure out where my program was leaking and it is the .SPEAK method in System.speech

I have converted an app that used the COM-based Speech objects to use the new System.Speech .Net library in .Net 3.5. Sounded like the right way to move forward to using all manged code within the .Net app. The app suddenly had a small mem leak.

I broke this down into 2 simple apps that convert "this is a test" to a WAV file of spoken words. One uses the COM-based speech objects, the other uses System.Speech. I ran them for 24 hours, each creating the WAV about 200,000 times.

COM based speech objects: no mem leak. Mem usage of app peaked at 13MB after about 40 minutes

System.speech: slow leak, nice and linear. Ran from about 14MB to 45MB in 24 hours

查看更多
霸刀☆藐视天下
3楼-- · 2019-04-06 10:32

This is a know issue in the Speak method. A structure called SPVTEXTFRAG gets created and never destroyed.

Details here : http://connect.microsoft.com/VisualStudio/feedback/details/664196/system-speech-has-a-memory-leak

查看更多
聊天终结者
4楼-- · 2019-04-06 10:33

I can give you a really simple answer for your question: Make the SpeechSynthesizer static!!!

I am quite sure that this will solve your issue.

Also - a tip ==>> every time you code, and you have a resource... use it as static and your life would be better!

查看更多
虎瘦雄心在
5楼-- · 2019-04-06 10:37

SendAsync() from the Ping also leaks. The solution there is to cast the sender as IDisposable first. So maybe the following also works here.

((IDisposable)spk).Dispose();
查看更多
登录 后发表回答