dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
14.91k stars 4.63k forks source link

Low memory handling? #6919

Open mogmios opened 7 years ago

mogmios commented 7 years ago

In CLR / .NET / C# is there any standard cross-platform way to receive a low memory notice before actually running out of memory? With Xamarin you can use DidReceiveMemoryWarning and there seems to be a couple methods specific to Windows that don't seem to work even on Windows.

MemoryManager AppMemoryUsageDecreased and most of the related functionality will compile ok but then throw mystery errors at runtime that don't seem to make any sense or be explained by Googling. Happened when registering any of the events and with most method calls.

I thought there might be some sort of event I could listen for but I'm not finding one. Simply marking fields with some sort of LowMemoryAction attribute seemed a possibility also but didn't find anything like that either.

For example, I have an object that computes an expensive operation for a FileSystemInfo object and which will run recursively to children if it represents a directory. I cache the result of the operation to speed up repeated calls but the operation can be redone without problem if need be. I'd like to be able to clear this cache if memory gets low. Currently using MemoryCache but it's not a very good solution.

Maoni0 commented 7 years ago

We don't provide anything like this from the GC. As you noticed, there are things from other higher level components that chose to provide such notifications themselves which may or may not work for you.

I am not aware of any xplat ways. You could of course detect memory load on the machine yourself (on Windows this is via GlobalMemoryStatusEx which is what GC uses).

ayende commented 7 years ago

Note that in Linux, for example, there is no way for the system to warn you that it is running out of memory. Instead, it will start swapping and then killing processes to free memory

ayende commented 7 years ago

Pretty much the only way we found to do that on Linux was to poll for memory status and raise our own events.

gkhanna79 commented 7 years ago

Is there anything actionable here?

Maoni0 commented 7 years ago

nothing actionable from the GC side.

gkhanna79 commented 7 years ago

Should the issue be closed in that case?

davidfowl commented 7 years ago

I think it's worth investigating what this event would look like from a cross platform POV. We recently had some design meetings about the ASP.NET cache where we decided to separate the detection of memory pressure from the cache itself. A cross platform component that can detect "low memory" would be super valuable for many scenarios. Do we know if other languages/platfprms/runtimes offer anything here?

@ayende what did you end up doing? Did you make it a library?

ayende commented 7 years ago

@davidfowl I'm polling: https://github.com/ravendb/ravendb/blob/v4.0/src/Raven.Server/ServerWide/LowMemoryNotification/PosixLowMemoryNotification.cs#L29

Note that I absolutely agree that a generic component will be useful here. Note that it also need to handle such things as containers' limited memory / jobs limited memory.

Maoni0 commented 7 years ago

on a related note, with hosting APIs (which unfortunately only exist on desktop) you can implement a pattern that says "as a host I decide that this is a low memory situation, so I will do all the clean up type of things (in the cache case it might mean to expire a bunch of cached entries), then I will inform the GC it should consider it a low memory situation", this helps the subsequent GC to reclaim more memory.

davidfowl commented 7 years ago

/cc @KKhurin

ayende commented 7 years ago

@Maoni0 Yes, if we can be notified by the host that we are running out of memory, or that a big GC is about to happen, we can do quite a lot to make it more efficient (removed pinned memory, for example), clear caches, etc.

gkhanna79 commented 7 years ago

@Maoni0 Are you suggesting that folks needing this notification write their own, instead of using the .NEt Core Host?

IMHO, .NET Core host should not participate in Runtime's policy of memory management or notification and this infrastructure is something that should be built into the runtime, enabling any future hosts to be able to take advantage of it.

Maoni0 commented 7 years ago

to be clear, it's not the host infrastructure that would participate in deciding what low memory looks like. it's the host that implements the hosting interface that decides when it wants the runtime to consider it's low memory. with hosting API you would register a ICLRMemoryNotificationCallback* with the IHostMemoryManager::RegisterMemoryNotificationCallback method. and the host would indicate if it wants the CLR to think it's low, normal or high on memory by implementing ICLRMemoryNotificationCallback::OnMemoryNotification. so when ICLRMemoryNotificationCallback::OnMemoryNotification says it's eMemoryAvailableLow, the finalizer thread will trigger a GC right away and GC will recognize this as "we are in low memory situation and should be more aggressive about doing GCs". additionally, the host can also implement IHostMemoryManager::GetMemoryLoad Method which tells GC what the percentage of memory load it wants the GC to think is.

I imagine this was how SQL used it (as most of these hosting interfaces were made for them). that was before my time. I am certainly open to revamping these interfaces if the current ones aren't sufficient/user friendly enough. of course to revamp hosting interfaces it would need collaboration from folks who own hosting.

mangod9 commented 3 years ago

would something like this https://github.com/dotnet/runtime/issues/6051 satisfy this requirement?

timcassell commented 2 years ago

If/when this makes it into the runtime, would it then make sense to add a SoftReference<T> type? Similar to WeakReference<T>, but only collects on low memory instead of any GC. This would be useful for an unlimited-timeout memory cache (as opposed to MemoryCache).