RazorEngine Memory Usage
Asked Answered
G

4

10

I have created a windows service to build and send emails. I am using the Razor Engine to parse the email templates. I am using a dynamic ExpandoObject to create the model.

My problem is when each email is created and sent the memory is increasing but it is never been released. I have profiled the service with Ants Memory profiler(I haven't used this before) but it is showing the following results:

With Razor Engine

Parsing 200 emails with Razor.Parse(text,model)

Generation 1: 12.9kb

Generation 2: 15.88mb

Large Object Heap: 290.9kb

Unused memory allocated to .NET: 3.375mb

Unmanaged: 69.51mb

Total number of memory fragments: 197

No Razor Engine

Returning 200 emails unparsed text.

Generation 1: 13.87kb

Generation 2: 3.798mb

Large Object Heap: 95.58kb

Unused memory allocated to .NET: 4.583mb

Unmanaged: 44.58mb

Total number of memory fragments: 7

With Razor the biggest generation 2 instances are:

System.Reflection.Emit __FixUpData[] - 2,447,640 live bytes, 3,138 instances

Has anyone any idea why the objects aren't being released and the Generation 2 is growing? Is there a way to have a new instance of the RazorEngine each time I want to parse a template and when its finished it will not be referenced and will go to the GC.

Ive tried creating a new instance of Template service each time I parse a template but this hasnt made a difference

using (ITemplateService templateService = new TemplateService())
{
     result = templateService.Parse<ExpandoObject>(text, model);
}
Glaze answered 6/9, 2012 at 13:45 Comment(8)
This is an old question, but but I don't think calling Parse() caches the template. What I've ended up doing is using Razor.GetTemplate() and calling Run() on the returned ITemplate instance. Still to see how much memory the assemblies take, but there should only be one assembly per template if it is cached properly.Stated
Have you successfully cached the templates? The memory in my service still increases and never gets garbage collected. Have you noticed a decrease in memory usage?Glaze
Currently im doing ITemplate template = Razor.Resolve(templateFileName, model); string result = template.Run(); But the memory still contunes to grow ridiculously.Glaze
Peek into the RazorEngine source. The template service uses a concurrent dictionary to cache templates. Make sure you are calling methods that use this cache. I know that using GetTemplate will check the cache first.Stated
I have yet to test for memory leaks in my app. It is a legacy webapp and needed significant extra development past just a simple call to razorengine.Stated
It looks like razorengine's Resolve method does in fact cache templates. You will not be able to negate the memory usage by the assemblies as they cannot be unloaded from the appdomain, but this shouldn't be enough to cause issue. The increased size of the Gen2 collection is trickier. It may just be a side effect of the intensive task of compiling the templates. I haven't used Ants profiler, but I would poke around to see how and why things are created, and maybe you'll find a memory leak, or maybe you'll find that it is just using that much memory out of necessity.Stated
Thanks. I will profile it again and try and see whats wrong. Its def got a memory leak somewhere because on our production environment the memory keeps increasing and has gone over 10GB before i restarted the service. Looking at the latest source. Parse calls GetTemplate and Run if the cacheName isnt nullGlaze
let us continue this discussion in chatGlaze
O
2

Each time you parse a template, RazorEngine compiles an in-memory assembly.
That can get expensive.

You should re-use your templates as much as possible.

Orlosky answered 6/9, 2012 at 13:47 Comment(5)
Why isn't it releasing the objects? What is holding a reference to them?Glaze
Maybe its overkill? Al I want is to parse a string with a dynamic model. I assumed that after each template is parsed, everything within razor will be released because it isn't needed?Glaze
If I cache all of the templates rather than parsing a string will this stop the memory from always increasing and never being released?Glaze
is an in-memory assembly created when a string is parsed or only when a name is given and the template is retrieved and cached. At the moment im using razor the first way.Glaze
@ministrymason: Only when the template is parsed.Orlosky
L
2

Old question, but to activate the template cache you must supply the "cache" argument to the Parse method (which can/should be the path to your template) :

return RazorViewService.Parse(File.ReadAllText(path), model, null, cache);
Lunnete answered 26/3, 2014 at 18:3 Comment(0)
M
1

If you're using the same template repeatedly call RazorEngine.Compile once then Razor.Run there after to ensure that the template is compiled only once.

Also I believe the memory the RazorEngine seems to leak memory when DEBUG is enabled in a build. Make sure your production code is built with the Release profile i.e. without the DEBUG compiler constant.

Magel answered 8/10, 2014 at 1:33 Comment(0)
S
0

When you compile a template, the dynamic assemblies that are created are loaded into the current appdomain. There is no facility to unload them so as you compile more templates, the memory keeps growing.

You can use the IsolatedTemplateService in RazorEngine 3.x to get around this. What it does is load the compiled template into a new appdomain. When that appdomain is garbage collected, the template assemblies loaded into that appdomain are then collected as well. There are some limitations though--such as the inability to use dynamic models (Expando objects) or anonymous models. The model also needs to be serializable.

Check this out from the author of RazorEngine: http://www.fidelitydesign.net/?p=473

Selfheal answered 6/10, 2012 at 23:18 Comment(2)
I am using a dynamic model so I cant use the IsolatedTemplateService. Is there no other way to stop the memory from continuously growing? Would it be better if i cached the templates? At the moment i retrieve the template and pass it to the RazorEngine as a string, rather than the template name.Glaze
What is the best way to use RazorEngine? The memory of the application ive wrote has grown to 2GB. I cant believe other people don't seem to be having problems.Glaze

© 2022 - 2024 — McMap. All rights reserved.