Constant Memory Leak in SpeechSynthesizer
Asked Answered
K

4

8

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.

Kisner answered 4/2, 2010 at 23:29 Comment(7)
you sure it keeps continually going up? memory will continue to go up in .net, until the GC comes through and cleans things up. unless it continually goes up and NEVER comes down, then i wouldn't worry about it.Imaginary
Yes, when I run this on Windows 7 it will eventually halt when attempting to create a new SpeechSynthesizer object. After the program halts, going to control panel and attempting to test the Text-To-Speech will result with the same. It will no longer speak until the machine is restarted.Kisner
what happens if you don't create a new SpeechSynthesizer object on each pass?Cabriole
Eric, I tried it that way the first time through and it is actually worse. This was why I tried creating and destroying the object with each call. It did improve slightly, but still doesn't solve the issue. Any call to SpeechSynthesizer leaves behind WAVEHDR and WaveHeader objects that grows the private memory until it crashes.Kisner
Will it help to call speech in a different AppDomain, and then periodically unload that domain, will it reclaim the memory?Regolith
Hi I have come across the same leakage problem due to Speech.Synthesis. Did you finally solve it? Thanks.Garwood
@DudeFx, do you experience the same leak in .NET 4.5 ?Regolith
P
5

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

Phillipp answered 17/9, 2011 at 19:41 Comment(1)
link is dead nowCinemascope
M
3

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

Mcnew answered 14/3, 2010 at 12:56 Comment(1)
I have also come across the leak and now solved by your hint.Garwood
L
2

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();
Lau answered 26/8, 2010 at 1:26 Comment(0)
B
0

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!

Burgoo answered 15/2, 2012 at 19:7 Comment(1)
I don't think instances are thread safe, you will likley have issues using a static SpeechSynthesizer instance across multiple threads.Diversify

© 2022 - 2024 — McMap. All rights reserved.