Doubts about memory management in .net

I am learning memory management in C # from the book "Professional C #"

Having a garbage collector means that you usually will not worry about objects that you no longer need; you simply allow all references to those objects that go beyond and allow the garbage collector to free up memory if necessary. However, the garbage collector does not know how free unmanaged resources are (for example, file handles, network connections, and database connections). When managed classes encapsulate directly or indirectly references to unmanaged resources, you must consider ensuring that unmanaged resources are freed when the class instance is garbage collected.

When defining a class, you can use two mechanisms to automate the release of unmanaged resources.

  • Declaring a destructor (or finalizer) as a member of your class.
  • Implementation of the System.IDisposable interface in your class.

I understood a little:

  • "unmanaged resources (such as file descriptors, network connections, and database connections)." What is connected with them? Why are they uncontrollable? (or) Why does the GC not manage these resources?

  • What code would we place in the finalizer or the Dispose () method of class a and how exactly would this code look like? Some examples of using these resources will be very helpful.

+4
source share
8 answers

The real question here is about urgency. Since the garbage collector explicitly tracks memory, it will know when to free memory by clearing objects without references. This can happen several times per minute or once per hour or even never (if you do not need to create new objects). But the important thing is that this happens when necessary.

But memory is not the only resource that is limited. Take the files. Usually, only one application at a time can open a file, as it can become messy if several people try to write to the same file. Databases have a limited number of connections. And so on. The garbage collector does not track any of these resources. And he does not know how to urgently close them.

Of course, you can open FileStream and read it without closing it later. if you miss the link to the object, the garbage collector will probably decide to collect the FileStream object that will run Finalizer, and the file will be closed correctly. But this can take a lot of time, and while the file is locked.

With a database connection, this is much more relevant since there is a very limited number of collections available, so if you open too many connections without getting rid of them, you will end up with an error because you will have a bunch of database objects. open connections that are waiting in line at the garbage collector.

Thus, proper disposal of a disposable item is good practice. Sometimes you can leave without doing it, but it's a bad style. If an object implements IDisposable, it's because it wants you to clear it when you're done with it.

+5
source

Some classes in the .NET platform are simply shells of the Windows API or third-party assemblies. These APIs are not code driven (they can be written in C ++ or they are old COM builds) and the garbage collector does not know when the application is no longer needed.

For example, when you open a disk file, it will remain open until you tell it to close the file. If you destroy the file pointer (i.e. leaving the area) without closing the file, this file will remain open and locked.

The Dispose method implemented in the Framework for these classes invokes the Close internal method, which is necessary for the final completion of the instance. Thus, all classes that wrap unmanaged code must implement the Disposable interface to ensure that the closure method that it implemented.

Then, when you instantiate this class, it is good practice to do this using the using statement, because when you leave the scope, the Dispose method is called automatically.

+10
source

1.) GC does not know how to properly close external resources. Of course, he could kill the network connection (this is, in fact, what he does if you don't disconnect, that is, the database connection). But the database is not notified when the connection is closed.

Similarly applies to file streams. Is there something in the buffer? Should this be written to the file before closing the file descriptor? GC does not know about it - the access code does.

2.) What follows. Therefore, if you have open file streams and an internal buffer - in the deletion method you must clear the buffer, write it to a file and close the hanlde file.

In normal mode, you do not have direct access to the databases. You use the libraries that manage this for you.

In most cases, it is enough to manage these external resource managers (Db connection, filestream, network classes) if your class is available.

+2
source

This is a good question and one that many developers do not seem to understand.

At a high level, managed resources are resources that are allocated and tracked using .Net. The memory used by the resource comes from the pool allocated for .Net, and the .Net runtime tracks all links between managed resources. This tracking (I'm sure this is the wrong term, but that’s enough) allows the .NET environment to run information that this resource is no longer in use and therefore can be released. Thus, unmanaged resources are resources allocated outside of this .Net managed pool and not monitored by the runtime. Most often these are links to the OS or external application resources. There are all sorts of complex reasons why the .Net runtime cannot “see” in an unmanaged resource, but I like to think of it this way: .Net is a wall-based garden that you should use to use. You can push a hole in this wall to see it from the outside (i.e. PInvoke), but you cannot own the resource from the other side.

Now, to the second part of your question. Bill Wagner has a great discussion on how to implement Dispose methods and why Effective C # in his book. There are also some really good answers about this here and here .

Hope this helps.

+2
source

Unmanaged resources are handles for resources that the operating system owns and controls (except for memory, of course).

GC does not clear memory immediately in the place where there are no more references to the object - it can leave it for a long time. If this were done with files, network and graphic descriptors, it would potentially take up a lot of operational resources and only free them from case to case.

To free these unmanaged resources back to the operating system, you need to explicitly free them by deleting them. Therefore, the use of IDisposable and the using keyword.

+1
source

I don’t like how the quoted text uses the term “unmanaged resources” because it assumes that this term refers primarily to objects that the OS knows about. In fact, I think it’s much more useful to think of an “unmanaged resource” as something outside the real object (perhaps outside the computer!), Whose useful life may exceed the life of the real object, the state of which may has been modified in such a way that it can cause problems if they are not cleared, and which are expected to clear the real object. A "managed resource" is a reference to an object that contains one or more "unmanaged resources", but which, as a rule, can take care of these resources (at least in the end), even if it is left behind.

Unmanaged resources are possible, even within fully managed code. As a simple example, an enumerator for a collection can subscribe to an event, so it will receive a notification if the collection changes. Meeting subscription list is an unmanaged resource. If the enumerator does not unsubscribe from the event before its refusal, the useful life of the collection containing the subscription to the event is likely to exceed the counter. Although an accidental closed subscription to events may not do too much harm, a procedure that creates many counters and leaves them without clearing the subscription can cause significant damage.

+1
source

In practice, I encoded a lot like with native code - C ++ - also known as unmanaged and managed code - C #. I'm still not sure why C # was invented in the first place - for developers, of course, there are many improvements, but behind the C # architecture there are quite a few hidden stones.

To my best understanding, many professional Microsoft developers have been given the task of making C #, and, as usual, comes with any new platform - the developers are overloaded with regard to their technology.

The first attempt, of course, was to say that "we are doing the right thing," and everyone else is doing it wrong - I think, because of this, an "uncontrollable" term appeared. It looks like we have “managed” code and something that was not designed properly is “un” -something :-)

Unmanaged resources, such as file descriptors, network connections, database connections, have been managed for quite some time - if you complete the process, it will close all file descriptors.

In C ++ you have malloc, for free, in C # you have a new one (or gcnew), but then you are struggling with the problems of linking what, why this object will not disappear from memory, what ram is - and most of the answers these questions are becoming quite difficult to answer.

The constructor / destructor is replaced by finalizers, destructors, disposable objects, where it is relatively difficult to check what is caused, in what order, and have you forgot to free all resources? "Oh, we succeeded, but we don’t know how to manage these objects ..." :)

C # is fun as long as it is a small and simple application - after you add 3D objects and a super huge number of distributions there, a lot of functionality, compilation will start to slow down and you are no longer satisfied with C # and think about returning in C ++.

In quite a few forums, you can probably find some debate about - is C # or C ++ better / faster / easier - and most people try to defend using any of these C #. The reality is that it is too complex, over an abstract and too heavy monster, and no one else can control it.

Intermediate language (IL) - represents one additional level of abstraction between the source code and the executable code (assembly), which makes it difficult to optimize and speed up your program.

But I'm also not a big fan of C ++ - the complexity of the language with pointers, links, and objects / classes does not simplify C ++ code or is easier to learn.

In principle, when you are building a new language, you need to take into account the infrastructure of the target language (low-level assembly + smooth integration with C ++ code and high-level code improvements), easy to use, easy to understand, easy to develop, easy to maintain, and improve).

Creating more “words” in theory can make the language richer - you can express yourself using less text and more efficiently, but again this does not prevent the language itself from being polluted (for example, IDisposable).

I intentionally compare a programming language with a natural language, because now I write the answer in a natural language, and it is more native to me than a programming language. However, a programming language is better structured than natural, and has no 2016-year history.

2 - finalizer / dispose - read this chapter at least 5 times, but still do not understand. Usually I create one function (close) and call it from both functions - from the finalizer - and from the order. Why try to understand something that is not important.

My recommendation to you anyway - try everything in the code - how it looks, how it looks. Books, as a rule, become something similar to the Bible - they push you into a religion in which you don’t want to be.

0
source

All Articles