开发者

Strange behaviour of C# windows service while >90% of RAM used

开发者 https://www.devze.com 2023-04-10 03:35 出处:网络
for some time I was trying to debug my algorithm that synchronizes data between two databases. Everything worked ok for a few months of everyday use and recently strange things started to happen eg:

for some time I was trying to debug my algorithm that synchronizes data between two databases. Everything worked ok for a few months of everyday use and recently strange things started to happen eg:

  • "DisableBuyButton" property was sometimes set to true, while this was explicitly set to false in the configuration file (I have stepped through the algorithm way too many times to find something odd - found nothing and while doing this on my machine everything was ok)
  • products were assigned to different categories than expected

and man开发者_Go百科y similar mistakes.

Then I remembered that on one CodeCamp meeting someone said that while debugging ASP.NET applications they had an issue with garbage collector that was in some kind of "panic mode" - this caused many unexpected and weird errors to occur.

I've checked what was going on with the amount of free memory - >90% was used. I resolved the issue simply by adding 1gb or RAM more to the virtual machine - all those weird things just vanished into a thin air.

Now the question: HOW IS THIS EVEN REMOTELY POSSIBLE?

//edit: Critical section to ensure only one instance is running:

        lock (this)
        {
            if (WorkStarted)
            {
                return;
            }
            else
            {
                WorkStarted = true;
            }
        }


There are too many factors involved to be certain, but one potential issue is silent fallbacks to default values (Say you attempt to load from config file every time the property is accessed, but return the default value if you can't).

Another issue and potentially the one you encountered is cache usage. You could have race conditions or time-outs that reset certain values to null which combined with the above can create interesting situations. This probably only happens in situations where there has been no access in quite some time (which usually cases the process to recycle and start fresh upon the next access) or when dealing with high memory situations.

Basically the garbage collector isn't going to flip bits at random on you (or delete accessible object) but it may do things that can appear as such after the layers of abstraction that we add.

EDIT:

Sorry for the long winded response, the topic is very complex, and I am trying to give generally applicable advice.

Lets take a simple example of a cache that orders everything with a last accessed time, and puts the oldest 20% by count into WeakReference objects in case the GC runs. You then go to get information from the cache, but since it might have expired, you check to see if it exists. This calls the HasValue property of WeakReference which returns true. Finally you go to read the object, but before you can the GC runs and kills the object. The cache gracefully returns null, but now your project has a null where it expected a value. Not to fear the exception handling routine detects an error and decides to return the default value instead (false in the case of a bool).

This all combines to create a situation where your code doesn't realize but it has the incorrect value, this problem could last a while if you don't call back to the cache for a while. Similar situations can occur if your cache responds to a GC call by removing data, potentially in the middle of a "safe" section of code.

The best solution to these kinds of issues is to follow your cache usage guidelines to the letter, or if you roll your own then make sure you understand any interacts you are using, such as the way WeakReference and the GC work, or the odd timing issues in hooking into the GC process.

For example using HasValue of WeakReference is not a good idea if you actually want the value. Instead you should just get Value, which will in the worst case return null, and test for null instead (or at that point you can call HasValue since the GC won't interfere if you have a reference to the pointed object, but kind of pointless when a null check will do)

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号