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.
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
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
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();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With