C
C#โ€ข2y ago
Ha4aJlbHuk_4a9l

โ” I somehow cause memory leak with Process.Start()

With this simple piece of code
using System.Diagnostics;

internal static class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("Started");
ProcessStartInfo info = new ProcessStartInfo { FileName = "echo", Arguments = "hi" };
Console.ReadLine();
for (int i = 0; i < 300; i++)
{
Process proc = new Process { StartInfo = info };
proc.Start();
proc.WaitForExit();
proc.Dispose();
}
await Task.Delay(int.MaxValue);
}
}
using System.Diagnostics;

internal static class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("Started");
ProcessStartInfo info = new ProcessStartInfo { FileName = "echo", Arguments = "hi" };
Console.ReadLine();
for (int i = 0; i < 300; i++)
{
Process proc = new Process { StartInfo = info };
proc.Start();
proc.WaitForExit();
proc.Dispose();
}
await Task.Delay(int.MaxValue);
}
}
when i publish and launch the program i can see in my system monitor (i'm on linux) that RAM usage just grows with each started process. E.g. with 100 starts that would grow from 30 to 43, with 300 it grows from 30mb to 54. I waited for some time but nothing was cleaned. GC.Collect() at the end of the program won't do anything, but(!) if i place it at the end of iteration the usage would grow 30 to 40 no matter how much times (i tried 100 and 500) i start the process which is how it should be. Am i doing something wrong here besides starting /bin/echo 500 times or that's some bug?
16 Replies
Ha4aJlbHuk_4a9l
Ha4aJlbHuk_4a9lOPโ€ข2y ago
I haven't learned how to do profiling stuff yet and honestly profiling 10 lines of code sounds bad
Jimmacle
Jimmacleโ€ข2y ago
why are you concerned about memory leaking here?
Connor
Connorโ€ข2y ago
You are not doing anything wrong. This is expected behavior based on how memory management and garbage collection work in .NET and in operating systems in general. When a new Process instance is created, it reserves some memory to store information related to that process.
internal static class Program
{
private static SemaphoreSlim semaphore = new SemaphoreSlim(10); // Allows 10 concurrent processes

static async Task Main(string[] args)
{
Console.WriteLine("Started");
ProcessStartInfo info = new ProcessStartInfo { FileName = "echo", Arguments = "hi" };
Console.ReadLine();
var tasks = new List<Task>();
for (int i = 0; i < 300; i++)
{
tasks.Add(StartProcessAsync(info));
}
await Task.WhenAll(tasks);
await Task.Delay(int.MaxValue);

}

static async Task StartProcessAsync(ProcessStartInfo info)
{
await semaphore.WaitAsync();
try
{
using (Process proc = new Process { StartInfo = info })
{
proc.Start();
await proc.WaitForExitAsync();
}
}
finally
{
semaphore.Release();
}
}
}
internal static class Program
{
private static SemaphoreSlim semaphore = new SemaphoreSlim(10); // Allows 10 concurrent processes

static async Task Main(string[] args)
{
Console.WriteLine("Started");
ProcessStartInfo info = new ProcessStartInfo { FileName = "echo", Arguments = "hi" };
Console.ReadLine();
var tasks = new List<Task>();
for (int i = 0; i < 300; i++)
{
tasks.Add(StartProcessAsync(info));
}
await Task.WhenAll(tasks);
await Task.Delay(int.MaxValue);

}

static async Task StartProcessAsync(ProcessStartInfo info)
{
await semaphore.WaitAsync();
try
{
using (Process proc = new Process { StartInfo = info })
{
proc.Start();
await proc.WaitForExitAsync();
}
}
finally
{
semaphore.Release();
}
}
}
You can limit the amount of processes like this
Ha4aJlbHuk_4a9l
Ha4aJlbHuk_4a9lOPโ€ข2y ago
I just use one CLI ipc client because i'm afraid i'll break even more stuff if i do it with sockets (i have some partial success tho) and i switch workspaces in https://i3wm.org/docs/ipc through ipc on a keybinding so i can launch processes pretty frequently Thanks, i'll try to understand what it does and my program is kind of a daemon so it's inacceptable to leak memory
Jimmacle
Jimmacleโ€ข2y ago
but is it actually leaking in practice? as in, if you run it for hours/days and use it normally does it continue allocating memory or are you basing your assumptions off this specific test?
Connor
Connorโ€ข2y ago
From what I can see, there is no leak. Just expected memory usage
Jimmacle
Jimmacleโ€ข2y ago
that's what i'm getting at
Ha4aJlbHuk_4a9l
Ha4aJlbHuk_4a9lOPโ€ข2y ago
if i hold the keybinding (with pretty frequent repeat rate) it grows by like 3mb/second
Jimmacle
Jimmacleโ€ข2y ago
how long do you do that? i don't know when the GC decides it's time to clean up
Ha4aJlbHuk_4a9l
Ha4aJlbHuk_4a9lOPโ€ข2y ago
how often should it clean up? i waited for like 5-10 minutes maybe not or not only the processes are guilty, i just tried separate suspected parts of program and what i discovered is Process.Start() probably leaking memory
Ha4aJlbHuk_4a9l
Ha4aJlbHuk_4a9lOPโ€ข2y ago
There is one more suspect which is process's redirected output reading there https://github.com/ShinyZero0/I3helper.cs/blob/main/I3IPC/I3.cs#L66. Afaik the stream's data should be cleaned after i read it tho ๐Ÿค” and i dispose everything that can be disposed, but calling GC there removed memory usage grow too
GitHub
I3helper.cs/I3.cs at main ยท ShinyZero0/I3helper.cs
C# program extending i3wm functionality. Contribute to ShinyZero0/I3helper.cs development by creating an account on GitHub.
Ha4aJlbHuk_4a9l
Ha4aJlbHuk_4a9lOPโ€ข2y ago
It's actually writing some data to disk and starting process too in Refresh() method but the method which starts process already has its own GC.Collect()
cap5lut
cap5lutโ€ข2y ago
I didnt check your code yet, but you should really read about how the GC works. even if the GC releases resources which are somewhere on the heap, that doesnt mean that it also shrinks the allocated heap sizes. https://learn.microsoft.com/en-us/dotnet/core/runtime-config/garbage-collector#high-memory-percent
Garbage collector config settings - .NET
Learn about run-time settings for configuring how the garbage collector manages memory for .NET Core apps.
Ha4aJlbHuk_4a9l
Ha4aJlbHuk_4a9lOPโ€ข2y ago
Oh thanks for that! I just unsuspended all my browser tabs to get 90+% memory load and the working set really dropped from 150mb to 20mb ๐Ÿ˜… GC seems to be smarter than me, i'll trust it more
cap5lut
cap5lutโ€ข2y ago
hehe, yeah it has to be smart also note that the memory of the spawned process isnt included here at all, its after all another process ;p
Accord
Accordโ€ข2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Want results from more Discord servers?
Add your server