#begin
Quite a while ago I finished this Udemy course on C# optimization tricks. At some point in this course there was a rather detailed video about the C# Garbage Collector (GC). It was such a refreshing video since, I too, took the GC for granted. I mean, I was well aware that I needed to avoid string concat, avoid boxing and use structs in some specific cases. However, just the information about the GC was really interesting and raised my awareness on how to properly interact with it.
After finishing the course on parallel programming I got this course in my recommendations. This is a course also made by Mark Farragher which I think is a great instructor. You just need to remember to play the video at 1.5x speed (or more). Due to the fact it is a short course and I think this is an interesting subject I enrolled and watched it’s content.
This review is going to differ a bit from the other Udemy reviews. In the previous reviews I talked about each section and video individually but since this course has quite some overlap with the C# optimization tricks course I reviewed in full detail, I will skip some parts and only task about the new stuff.
Nonetheless, I viewed all content in this course as if it was a new course. So I watched some of Mark’s video’s over 4 times now already.
Course layout
So let’s quickly discuss the layout of the course, what I’ve already discussed in the C# optimization tricks review and what is actually brand new content.
The course has 5 sections; an 1 Introduction, 2 Fundamentals of the .Net framework, 3 A Detailed look at garbage collection, 4 Simple tricks to improve memory allocation in your code, 5 Final words.
The first section is where Mark introduces himself. The second section explains about the fundamental aspects of the .Net framework. In this section Mark talks about the stack, the heap, value and reference types and boxing and unboxing. The third section is the first section that has some brand new content which is the core of the course; A detailed look at garbage collection. This is probably where most of this review is going to dive into. Section 4 is about simple tricks to improve memory allocation. This section contains video’s that are also discussed in the C# optimization blog. However there are also some new video’s here. The last section is where Mark says his goodbye’s and wishes you good luck on your programming journey.
So, for this review there are only two sections left; Section 3 A Detailed look at garbage collection, and, 4 Simple tricks to improve memory allocation in your code.
Section 3: A Detailed Look At Garbage
Collection
The new section in this course starts off with two video’s about the garbage collector that were included in the C# Optimization course. The first video gives some general information about Garbage collection in .Net. This is also where Mark explains about the fact that the GC in .Net is a generational GC, how the lifecycle of data works and long and short-lived objects. The second video is about how to optimize your code to align to these assumptions made by the GC. Mark gives some information about defining and initializing data just before you need it instead of defining it way up front.
Then finally, the third video of the third section is a brand new video. This video is about something called Finalizers a.k.a. destructors. Mark explains that the finalizers should absolutely be threadsafe since they are called on the GC thread, and not the main thread. Additionally, finalizers cannot reference other objects since they might already have been cleaned up and most importantly, they are not guaranteed to be called since a program on has generally only 4 seconds in total to be cleaned up. Objects that are not destroyed properly are simply deleted from the stack and heap.
This video was very helpful to me; I can remember very fondly that I had some bugs in my code because I did some events unassignments in the destructors of classes. Since they are not guaranteed to be called, my bugs make sense now. I think some colleague warned me about it too. Also, what I did not realize at first is that the finalizers are called on another thread so that might create bugs when having the expectation that they run on the main thread. And another thing is that I did not really know it was bad practice to call other objects in the finalizers. This makes sense though, since the finalizer should be encapsulated.
The next and last video in this section is about the dispose pattern, this can be implemented by simply implementing the IDisposable interface. With the dispose pattern you can explicitly write code that runs before the object is finalized. Also, if you are use you class with the using statement the dispose function will be called automatically. The cool thing about the dispose pattern is that you can differentiate between when the function is called by a user, or by the finalizer. If it is called by a user, you are allowed to reference other objects and do whatever you want in the Dispose method. If it is called by the finalizer, the conditions explained in the previous video apply. Also, having an implementation of the dispose function will prevent objects lifetime extending phase 1 of the garbage collector.
Section 4: Simple Tricks To Improve Memory Allocation In Your Code
The fourth section starts with a video about boxing and unboxing. I’ve discussed this in great detail in my blog about C# optimization tricks so I wont discuss it again. The same goes for the second video, which is about string concatenation. Always use the stringbuilder if you are concatenating lots of strings and avoid using the + operator.
The third video is a new video and it is about using structs over classes. Let’s take a look: The video starts of with some basic information about what struct are exactly. They are value types, not reference types and can live either on the stack or heap depending when and where they are created. Structs can’t inherit other structs or classes, but they can implement interfaces. And they cannot have finalizers, parameterless constructors or field initializers. Then Mark explains the video behind the fact that a struct cannot have a finalizer and the GC does not have to explicitly clean it up.
When a struct is created inside a method it will automatically be destroyed when the containing stack-frame is deleted. And when it is created in a reference type is will be destroyed when the reference type is collected by the GC. So, the GC does not have to take structs into consideration making a lower memory footprint for your program.
Now, you might wonder, why don’t I always use structs instead of classes? Well there are some issues that can arise when you are using structs. For one, structs are immutable, which in an OOP program might not be very useful since you want to maintain references to objects proeprly. Structs can also be prone to boxing and unboxing. Additionally, you should use structs if they represent a single value, and identity can be compared based on that value and if you are creating many (thousands or millions) of instances. Structs only reserve memory exactly for their internal fields and do not have the 16 byte overhead that classes do. So when you create many of them, this is going to save you a lot of memory.
The next video was also discussed in the C# optimization course. This one is about to always pre-initialize your collection if you know (roughly) how many elements it is going to contain. This will most likely save you a lot of memory since the collection types in .Net will automatically resize them selves when they are full. This resizing algorithm always simply doubles the capacity of the list, and thus, you might end up with almost the double amount of allocated list space than you actually need.
The last new video in this course is about avoiding to call ToList(), or ToArray() while using Linq in your algorithms. Calling these functions will convert the entire other IEnumerable into a list, reserving the memory again. This will probably give you some unexpected memory inflation on your queries. To solve this problem you should call ToList() before you run your query. Then the memory is allocated only once and you program will run much faster. The example in the video is about a simple implementation of a spellchecker. Mark is able to speed the spellchecker up with a factor of 25x by just removing the call to ToList() in the query for correct spelling.
Conclusion
This review was pretty short. This is because the course contains a lot of recycled materials form the C# Optimization Tricks course. I totally understand this from Mark’s point of view but I would not be really satisfied if I had bought this course for the full price. Then again, the content in here is really high quality, easy to understand and to the point. Also, this course would not be complete without the duplicated content so I can’t stay mad at Mark forever.
I have learned some new things about the dispose pattern, finalizers and I will probably not forget to pre-initialize my collections before adding lots of elements. I used to just implement the parameterless dispose function, but now I will probably fully implement the dispose pattern since I can differentiate between the manual disposing of an object and it being finalized by the GC. This will probably result in some cleaner code and less memory footprint.
To sum this review up. I do recommend this course to any .Net developers, of any skill level. Even though the content is very much the same as the content in the C# optimization tricks course it is very complete. I would recommend to first view this course, and then dive into the optimization tricks course to lessen the pain of the duplicate content. The other course has lot’s of material that is not discussed in this course so I think you might not bother as much when you view the courses in this order.
#end
01010010 01110101 01100010 01100101 01101110
Recent Comments