Easily reproducable memory leak

SyntaxEditor for Windows Forms Forum

Posted 10 years ago by Matt Whitfield
Version: 4.0.0280
Platform: .NET 2.0
Environment: Windows XP (32-bit)
Avatar
Ok, hopefully this one has an easy fix. In my app I construct DynamicSyntaxLanguage objects in another thread and apply them to the SyntaxEditor.Document when it's ready. Unfortunately this has two nasty side effects - 1) it gets progressively slower over time and 2) it leaks (a lot).

This is code in the MainForm.cs of the VS2008 sample application that comes with .280 -

// does not leak
private void helpAboutMenuItem_Click(object sender, System.EventArgs e) {
    for (int i = 0; i < 20; i++)
    {
    editor.Document.LoadLanguageFromXml(@"C:\Program Files\Actipro Software\WindowsForms\SyntaxEditor\v4.0.0280\TestApplication-CSharp.VS2008\Languages\Dynamic\Lexers\ActiproSoftware.SQL.xml", 0);
    DynamicSyntaxLanguage dsl = editor.Document.Language as DynamicSyntaxLanguage;
    DynamicLexicalState dls = dsl.LexicalStates[0] as DynamicLexicalState;
    LexicalPatternGroup lpg = dls.LexicalPatternGroups[0];
    for (int j = 0; j < 1000; j++)
    {
        lpg.Add(new LexicalPattern(j.ToString()));
    }
    }
}

// leaks
private void helpAboutMenuItem_Click(object sender, System.EventArgs e) {
    for (int i = 0; i < 20; i++)
    {
    DynamicSyntaxLanguage dsl = DynamicSyntaxLanguage.LoadFromXml(@"C:\Program Files\Actipro Software\WindowsForms\SyntaxEditor\v4.0.0280\TestApplication-CSharp.VS2008\Languages\Dynamic\Lexers\ActiproSoftware.SQL.xml", 0);
    DynamicLexicalState dls = dsl.LexicalStates[0] as DynamicLexicalState;
    LexicalPatternGroup lpg = dls.LexicalPatternGroups[0];
    for (int j = 0; j < 1000; j++)
    {
        lpg.Add(new LexicalPattern(j.ToString()));
    }
    editor.Document.Language = dsl;
    }
}

// leaks (less)
private void helpAboutMenuItem_Click(object sender, System.EventArgs e) {
    for (int i = 0; i < 20; i++)
    {
    editor.Document.LoadLanguageFromXml(@"C:\Program Files\Actipro Software\WindowsForms\SyntaxEditor\v4.0.0280\TestApplication-CSharp.VS2008\Languages\Dynamic\Lexers\ActiproSoftware.SQL.xml", 0);
    DynamicSyntaxLanguage dsl = editor.Document.Language as DynamicSyntaxLanguage;
    dsl.IsUpdating = true;
    DynamicLexicalState dls = dsl.LexicalStates[0] as DynamicLexicalState;
    LexicalPatternGroup lpg = dls.LexicalPatternGroups[0];
    for (int j = 0; j < 1000; j++)
    {
        lpg.Add(new LexicalPattern(j.ToString()));
    }
    dsl.IsUpdating = false;
    }
}
Is there something I'm missing?

Comments (8)

Posted 10 years ago by Matt Whitfield
Avatar
Just some more info

Maybe the third method doesn't leak less - I changed the 1000 to 10000 in one of my tests - so that probably explains that.

Also, not sure if it helps, but the profiler I'm using says that the extra objects are held 'only by event handlers'...
Posted 10 years ago by Matt Whitfield
Avatar
Some more info...

First off it leaks less than I thought in the above examples - I missed the GC.Collect().

However - I have found out how I made it leak a lot more in my app... In the separate loading thread, I was loading the syntax language into a syntax editor created just on that thread - with the following code

using (SyntaxEditor editor = new SyntaxEditor())
{
  editor.Document.Text = "SELECT * FROM [sys].[objects]";
  editor.Document.Language = SyntaxLanguage;
}
The reason I was doing that is because the line editor.Document.Language = x took a lot more time without it. I guess I am missing something and your words of wisdom would be much appreciated!
Posted 10 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Matt,

For issues like this, please always build a simple sample project that shows the issue and email it over so that we can debug it. Make sure the sample project is bare bones and shows nothing other than this issue occurring. Once we receive that we can look into it further.

Also, two things...

First, whenever you are modifying a language you should always wrap the language-changing code with the IsUpdating = true (before) and IsUpdating = false (after).

Second, make sure you always try to repro issues on the latest maintenance release before submitting anything, as problems could have been fixed already. I see you are using an older build and we have done memory work since then, which could affect this issue.

I did a quick test and didn't see any leaks here. Maybe it has already been fixed per this post:
http://blog.actiprosoftware.com/post/2009/07/13/Latest-UIStudio-and-SyntaxEditor-for-WinForms-builds-help-prevent-memory-leaks.aspx


Actipro Software Support

Posted 10 years ago by Matt Whitfield
Avatar
Thanks

I will get the latest build, build a project and send it over if I can still reproduce the issue...

Edit2 -> removed some other questions.

Edit ->

Sorry, one more question. I am trying an alternative route of managing the list of object names myself - and providing the relevant highlighting through the GetHighlightingStyle method of DynamicOutliningSyntaxLanguage. However - that just passes me in an IToken, which doesn't give me the text of the token itself. So how should I go about getting the text for the token, or should I use a different override for providing custom highlighting? I basically want to use some code like the following:

public override HighlightingStyle GetHighlightingStyle(IToken token)
{
  if (token.Key == "IdentifierToken")
  {
    return _getHighlightingStyle(token.Text);
  }
  else
  {
    return base.GetHighlightingStyle(token);
  }
}
Edit2 -> Added some background info:

The reason I am doing this is because I want to reduce the overall memory usage of the application - and because identifiers in my language can exist in three states (Default - <identifier>, SquareString - [<identifier>] and QuotedString - "<identifier>") it means that the memory usage just for providing the syntax highlighting runs to about 60 megs, which I'm keen to avoid.

I am prepared that the answer may well be 'you need to derive your own lexer & language from SyntaxLanguage'...

Thanks again for all your excellent support!

[Modified at 09/09/2009 04:25 AM]
Posted 10 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Matt,

I don't think that changing how the dynamic language does syntax highlighting (via GetHighlightingStyle) will help you on memory at all. Right now it just does this, which is faster than what you were working on:
return token.HighlightingStyle;

If you are going to be handling very large documents, which is what it sounds like, using a programmatic lexer like our Simple language sample will definitely use less memory. Dynamic languages use the most memory and the ability for a language to support merging with other languages uses memory too (dynamic languages have that feature by default). The leanest language type memorywise would be a language that directly inherits SyntaxLanguage, has a programmatic lexical parser, and has its tokens inherit TokenBase (not MergableToken).


Actipro Software Support

Posted 10 years ago by Matt Whitfield
Avatar
Thanks for all your help.

I still have two questions though

1) Is there a method that I can over-ride to change syntax highlighting in the way I described?

The reason I am asking is because initially I am less concerned about the per-token storage overhead, and more concerned about reducing the overhead of loading the syntax language in the first place - 3 lots of 20megs worth of text (because the patterns/groups have to exist in 3 lexical states) is a big part of my overall memory usage, even with little/no text in the editor.

2) What is the best way to replicate the 'trigger' functionality of a dynamic language when using a syntaxlanguage derived language - is it to handle the keys in 'keytyped'?

Thanks again for all your help - I *really* appreciate your support.
Posted 10 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Matt,

20MB of text in a language definition itself? Yikes! You really would be best off doing a low-level language implementation with programmatic lexical parser if that is the case. That would make it trivial to reuse your keyword lists, etc.

You got it, our triggers are essentially kicking off in KeyTyped, examining the current state and firing if the key and state match.


Actipro Software Support

Posted 10 years ago by Matt Whitfield
Avatar
Thanks for all your help. I'll begin on that lexer then :)
The latest build of this product (v2018.1 build 0341) was released 6 months ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.