• Visitors can check out the Forum FAQ by clicking this link. You have to register before you can post: click the REGISTER link above to proceed. To start viewing messages, select the forum that you want to visit from the selection below. View our Forum Privacy Policy.
  • Want to receive the latest contracting news and advice straight to your inbox? Sign up to the ContractorUK newsletter here. Every sign up will also be entered into a draw to WIN £100 Amazon vouchers!

.NET and destructors!!!!!!!!!!!!!

Collapse
X
  •  
  • Filter
  • Time
  • Show
Clear All
new posts

    #11
    Yes I started using IDisposable for a long time, but I also had destructor - this was the undoing as GC seems to have queued them for destruction in a separate thread that was taking AGES to execute under some circumstances that I managed to re-create.

    A very good post on this topic can be found here, with a follow up article here (as im sure youve found Atw)... there are some interesting comments to the second post.
    Vieze Oude Man

    Comment


      #12
      Originally Posted by AtW
      Which was exactly the problem - I had Dispose called for all objects, but they were not removed from heap because objects had consturctors, so GC in its wisdom thought to keep them around since it assumed that just because machine has got 2 GB of RAM its okay to eat 300 MB of it for no reason - crap GC design. If programmer explicitly calls GC.Collect() then GC should either collect garbage or new developer responsible for GC should be hired.
      Mmmm.... read the aritcles I pointed to and see if that makes sense regarding the different levels of Garbage Collection, and try some profiling to see the various amounts of GC activity / queues you have at each level.... sounds like you have a lot of 'dead' objects promoted to gen2 which might be related to the way references are being held / released (crap code - either correct the code or a new developer for SKA should be hired! )

      As an aside if you really do want to use finalisation, the only way to achieve 'deterministic finialisation' (immediately calling the destructor without waiting for GC) is with COM+, applying the [JustInTimeActivation] attribute.

      The destructor is called when the client returns control of the object (deactivates). Of course there is an overhead for this, although with Object Pooling this could be mitigated perhaps. Bear in mind you still have to wait for the GC to run to 'free' memory', but presumably you will prevent promotion of 'dead objects' to a higher 'gen' level.
      Vieze Oude Man

      Comment


        #13
        Originally posted by cswd
        Rewrite it in forth
        One of the more sensible posts this year.
        Insanity: repeating the same actions, but expecting different results.
        threadeds website, and here's my blog.

        Comment


          #14
          Look mcquiggd, I've seen this articles and I do not normally use gc collect - however when I have a situation when memory is not being reclaimed and will be growing and growing beyond level I consider acceptable, then if gc.collect helps (and it does) then I use it - the article can say whatever it wants, but if bloody thing does not work on its own and explicit call helps considerably then I will use it.

          Objects with destructors seem to be treated very differently in SOME situations - this situation arise in .NET 1.1 when you use a COM hack to increase number of threads in ThreadPool - I have no idea why this triggers the issue with GC, but it does seem to change some of its strategy - this seems to happen if MSCREE.DLL is from v2.0 of .NET - the darn thing is supposed to co-exist with v1.1 but in actuality they seem to replace that key file.

          So, the score is this - GC.Collect() does help reclaim memory - if I don't call it in a number of routines they will use a LOT more memory. OF course I've got 2 GB RAM, so in theory GC may be making rational choice not to clean up straight away since loads available, but this logic totally misses psycological point of view of customers - if they see your app spiking to 300 MB used, then it will look a LOT worse, then if app will run 1% slower due to more frequent GC collects, but never exceed 100 MB.

          I do like idea of GC and its fine by me that it collects on its own, however as a programmer I do know far better than Microsoft or anybody else if I wish to free up memory by collecting garbage - the only thing Microsoft can tell me is that it will cost me % milliseconds of extra work and then I can make a decision how often I want to force collect. They did the right call with points (you can do them if you want, but you need to be explicit about it), and they should do the same with GC.
          Last edited by AtW; 2 April 2006, 13:27.

          Comment


            #15
            Of course GC.Collect will free memory....

            However the issue seems to be whether or not you should be using destructors at all... perceived wisdom is that you shouldnt and the articles explain why, as this interferes with the normal running of the GC, which seems to tie in with the behaviour you have witnessed... (and as mentioned in my original post I assumed you had read these already - I realise you are not stupid Alexei - unless you want to place another bet! ).

            Is there a specific reason why you are implementing destructors and not using IDisposable and allowing the GC to run as designed...?

            Have you actually profiled the application to see what is happening with respect to pending queues for garbage collection....?
            Vieze Oude Man

            Comment


              #16
              Originally posted by mcquiggd
              Of course GC.Collect will free memory....
              Not always - but it does in some big cases so if it takes you 10 secs to do some heavy processing then spending 0.1 sec on cleaning memory is a reasonable cost in my view.

              Originally posted by mcquiggd
              However the issue seems to be whether or not you should be using destructors at all... perceived wisdom is that you shouldnt and the articles explain why, as this interferes with the normal running of the GC, which seems to tie in with the behaviour you have witnessed...
              The issue I have is this - .NET 1.1 seems to be okay with it, but in one speficic circumstance (using COM hack to increase number of max threads in threadpool) it would change this behavior - there is absolutely no documented reason for this change in GCs behavior, very annoying.

              I actually switched to IDisposable interface, however mere presense of destructor (ie ~Class() {}) would make GC classify it differently but again only if I forced .NET to inrcease number of threads in threadpool - .NET 2.0 seems to have same behavior by default and it seems that this crap happens only if .net 2.0 is installed along line.

              I've spend lots of time profiling but ended up finding it manually.

              Comment


                #17
                Sorry Alexei, but just to clarify, what is actually happening in the destructor you have created...?

                The increase in the thread pool, I presume is done using unmanaged code (as you mention a COM 'hack') .. is this still the case when you use .Net 2 or is that only implemented in the original 1.1 based version ..? At what point is this code called...?
                Vieze Oude Man

                Comment


                  #18
                  Originally posted by mcquiggd
                  Sorry Alexei, but just to clarify, what is actually happening in the destructor you have created...?
                  The destructor clears up internal variables, normally byte[] arrays but also Hashtables/ArrayLists - I found that a lot of memory leaks can happen when you keep some other objects inside Collections.

                  My destructors were dealing with managed memory - nothing unmanaged. I switched to IDisposable recently, but this glitch in .NET really bugged me - some people reported very high memory usage that I could not reproduce, but in the last few days I managed to trace it to seeminly unrelated code, see it here: http://www.codeguru.com/csharp/cshar...cle.php/c5847/

                  I found that my PC was leaking memory like a sieve if I used this code to increase number of threads in threadpool - something that is absolutely necessary as some clueless person created SetMin/GetThreads functions in Threadpool but did not add SetMax - this was only done in .NET 2.0.

                  So, if I used that thread unlocking code than suddenly GC's behavior changed big time - now why the $$$$k would that be? No idea, but this code was somehow triggering new GC's behavior which was to keep objects with explicit destructors (~Class) for much longer, almost as if it leaked them - which I think it did. This seems to happen only if MSCOREE file was from .NET 2.0 - in theory 1.1 and 2.0 can be side by side, but in practice they actually replace that core file - $$$$$$s!

                  Moral of the story is this - do NOT use ~ destructors in C# - use IDisposable interface and Dispose your objects explicitly to be sure that %%%%%%ing magic memory mananged process %%%%%% will actually work.

                  Comment


                    #19
                    Mmm.. admittedly the side by side install of .Net 2 appears to be flawed... might be possible to the specify the location of a version of the runtime in a config file and explicitly use that but frankly I cant see the point...

                    The thread unlocking code is unmanaged, presumably causing the different behaviour of the GC as you had explicit destructors..

                    Collections (or any reference type) implemented as instance Fields will have their variables (i.e. the value types that indicate the position of the actual object) stored in the same context of the class, i.e. on the heap rather than the stack... logically this would mean that nesting of references in this way would lead to the GC promoting multiple 'dead' nested object references to gen2 as the original reference is probably still around over several GC calls, and the gen2 is garbage collected less often. Forcing it to perform a full garbage collection will indeed clear down memory properly. Collections created locally within Methods (or passed as parameters by value) will be implemented as value types on the stack referencing a memory location on the heap and will be cleared down much more often. You are probably only noticing this behaviour as you have an application with such a 'heavy duty' usage... most day to day applications will behave adequately.
                    Last edited by mcquiggd; 2 April 2006, 15:08.
                    Vieze Oude Man

                    Comment


                      #20
                      Originally posted by mcquiggd
                      Mmm.. admittedly the side by side install of .Net 2 appears to be flawed... might be possible to the specify the location of a version of the runtime in a config file and explicitly use that but frankly I cant see the point...
                      MSCOREE is in system32 dir - looks like .NET 2.0 replaces previous version.

                      Originally posted by mcquiggd
                      The thread unlocking code is unmanaged, presumably causing the different behaviour of the GC as you had explicit destructors..
                      There is no reason why GC should change its behavior due to ThreadPool having increased number of threads that it can use, yet, I traced the issue with 100% replicability to usage of that thread unlocking code.

                      Originally posted by mcquiggd
                      Collections created locally within Methods will be implemented as value types on the stack referencing a memory location on the heap and will be cleared down much more often.
                      I have classes that contain collections as class variables that contain references to other objects - I am pretty heavy object oriented nowadays.

                      The truth is that in some cases GC.Collect() will clear lots of memory and if you not using it then you will face more memory used - this is a serious issue for cases when you have normal people running your code, also non-clearup of objects increases fragmentation, so the guy who says don't use GC.Collect() at all is seriously wrong - there are cases when usage of GC.Collect() is very beneficial and since he is supposed to know such cases better than me, he should have documented that. And certainly they should have made sure that programmer has complete control over GC - if I say collection all garbate then it should do just that - collect it.

                      Comment

                      Working...
                      X