-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix the memory surge caused by script running and not being GC controlled for a long time #6145
base: main
Are you sure you want to change the base?
Conversation
…ontrolled for a long time
To my knowledge, this changes effectively nothing. |
@Suchiman I have tested it before, before adding this operation,It will cause the server to run out of memory and crash. GC is in effect, but it seems that the situation of freeing memory is not good. |
@fanslead Are you sure the difference in GC activity is caused by setting the local variable scriptState to null? Because that really makes no sense given how GC works - at least, how I think I know it works. The local variable will cease to exist as soon as the method returns. What would be more logical is if the caller of that method holds on to the returned value. |
@fanslead A more logical explanation for the increase in memory consumption is the fact that the scripts are cached. The larger the number of scripts, the larger the amount of scripts being stored in cache. To see if this is the case, maybe try and see what happens when you update the following method: private Script<object> GetPreCompiledScript(Script<object> script)
{
var cacheKey = "csharp:script:" + Hash(script);
return memoryCache.GetOrCreate(cacheKey, entry =>
{
if (_csharpOptions.ScriptCacheTimeout.HasValue)
entry.SetSlidingExpiration(_csharpOptions.ScriptCacheTimeout.Value);
return script;
})!;
} Remove the caching logic: private Script<object> GetPreCompiledScript(Script<object> script)
{
return script;
} |
@Suchiman I just noticed that the caching of the Script object might be ineffective, because the script is always created, and then cached: private Script<object> GetPreCompiledScript(Script<object> script)
{
var cacheKey = "csharp:script:" + Hash(script);
return memoryCache.GetOrCreate(cacheKey, entry =>
{
if (_csharpOptions.ScriptCacheTimeout.HasValue)
entry.SetSlidingExpiration(_csharpOptions.ScriptCacheTimeout.Value);
return script;
})!;
} Notice that the script object is simply passed in as an argument, and then added to te cache before being returned. |
@sfmskywalker When by pointing scriptState to a null reference and Immediately execute GC forced recycling. Memory can be stably released. But immediate GC will lead to increased CPU consumption.
But,When I do not change any. The server memory quickly fills up and crashes |
I try caching the script. Of course, it is effective, but if there are a large number of different scripts and the memory is not released for a long time, it will also fill up the server's memory and cause it to crash. Meanwhile, when running with cache, there will also be a slight increase in memory, about 5-10MB. If cache is not used, the memory growth during each compilation and execution of the script is approximately 200Mb-300Mb |
Ok. I'd like to do some experiments as well. In order to reproduce the memory consumption, what's the number of C# scripts we're looking at, give or take? |
That is correct, i would have had to rewrite the entire scripting engine to make it not depend on the |
Caching of the scripts is a very recent addition (new in 3.3-RC3, released 14h ago), was the leak observed with or without the caching? Not caching the scripts might actually lead to memory exhaustion, because i'm not sure, if the dynamically generated assemblies from the scripting engine are ever freed. IIRC they didn't use to, because roslyn couldn't generate collectible assemblies so they stuck around forever, but i don't know if that changed. |
@Suchiman I think caching scripts can certainly suppress memory growth to a certain extent. However, cached scripts will not be released by GC, and when the number of different scripts reaches a certain level, the memory will still be full.The best solution should be to ensure that the script is promptly reclaimed by GC.Of course, my PR plan can only slightly accelerate the timing of script recycling by GC |
@Suchiman @sfmskywalker I made a mistake. The server environment and VS Debug environment are different. When I force GC in VS, the memory can be stably released, but in the server environment, this effect doesn't look good. |
When there are a large number of C # scripts, frequent compilation and running of scripts can cause memory to skyrocket and not be released by GC for a long time.It will cause the server to run out of memory and crash.
By pointing scriptState to a null reference to break the reference relationship, it can be released by GC as early as possible, thereby reducing memory resource consumption.
This change is