In the C programming language, you're regularly forced to deal with the painful, dangerous concepts of pointers and explicit memory allocation.
b1 = (double *)malloc(m*sizeof(double));
In modern garbage collected programming languages, life is much simpler; you simply new up whatever object or variable you need.
Double[] b1 = new Double[m];
Use your objects, and just walk away when you're done. The garbage collector will cruise by periodically, and when he sees stuff you're not using any more, he'll clean up behind you and deal with all that nasty pointer and memory allocation stuff on your behalf. It's totally automatic.
Pretty awesome, right? I'd wager the majority of programmers alive today have never once worried about malloc(). I call this progress, as does Jamie Zawinski:
Based on my experience using both kinds of languages, for years at a stretch, I claim that a good garbage collector always beats doing explicit malloc/free in both computational efficiency and programmer time.However, I also claim that, because of the amount of programmer time that is saved by using GC rather than explicit malloc/free, as well as the dramatic reduction in hard-to-debug storage-management problems, even using a mediocre garbage collector will still result in your ending up with better software faster.
Most of the time, throwing memory and CPU at the problem is still cheaper than throwing programmer time at the problem, even when you multiply the CPUs/memory by the number of users. This isn't true all the time, but it's probably true more often than you think, because Worse is Better.
But even for programmers who have enjoyed automatic garbage collection their whole careers, there are still some.. oddities. See if you can spot one here:
sqlConnection.Close(); sqlConnection.Dispose(); sqlConnection = null;
That is one hellaciously closed database connection. Why don't you take it out back and shoot it, while you're at it?
Even with your friendly neighborhood garbage collector making regular rounds on commodity desktops/servers where many gigabytes of main memory are commonplace, there are still times when you need to release precious resources right now. Not at some unspecified point in the future, whenever the GC gets around to it. Like, say, a database connection. Sure, your database server may be powerful, but it doesn't support an infinitely large number of concurrent connections, either.
The confusing choice between setting an object to null and calling the Dispose method doesn't help matters any. Is it even clear what state the connection is in after Close is called? Could the connection be reused at that point?
Personally, I view explicit disposal as more of an optimization than anything else, but it can be a pretty important optimization on a heavily loaded webserver, or a performance intensive desktop application plowing through gigabytes of data.
Of course, your average obsessive-compulsive developer sees that he's dealing with a semi-precious system resource, and immediately takes matters into his own hands, because he can do a better job than the garbage collector. K. Scott Allen proposes a solution that might mollify both camps in Disposal Anxiety:
What the IDisposable interface needs is a method that promotes self-efficacy in a developer. A method name that can stir up primal urges as the developer types. What we need is a method like the one in BSD's shutdown.c module.
die_you_gravy_sucking_pig_dog() { char *empty_environ[] = { NULL }; syslog(LOG_NOTICE, "%s by %s: %s", doreboot ? "reboot" : dohalt ? "halt" : dopower ? "power-down" : "shutdown", whom, mbuf); (void)sleep(2); (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n"); if (killflg) { (void)printf("\rbut you'll have to do it yourself\r\n"); exit(0); }Now, I know this function was written back in the days when steam engines still ruled the world, but we could modernize the function by applying some .NET naming standards.
sqlConnection.DieYouGravySuckingPigDog();Can you feel the passion behind this statement? This statement carries the emotion that is hard to find in today's code. I hope you'll support this proposal. Good people will be able to sleep at night once again.
So the next time you feel anxious about letting objects fall out of scope, remember: you could always terminate them with extreme prejudice, if you feel it's necessary.
But it probably isn't.
Bringing things full circle, the phrase "Die, you gravy sucking pig dog!" is likely derived from a routine in Steve Martin's 1978 "A Wild and Crazy Guy" album:
--
his first writing experience was composing cheers for the team. "But the other cheerleaders," he tells audiences, "were so jealous they wouldn't use my cheers. I wrote, 'Die, you gravy-sucking pigs'."
--
Shame, we can almost get there....
using System;
class SqlConnection: IDisposable
{
public void Dispose() {}
public void Close() {}
}
static class SqlConnectionExtensions
{
public static void DieYouGravySuckingPigDog(this SqlConnection connection)
{
connection.Close();
connection.Dispose();
}
}
class Program
{
static void Main()
{
SqlConnection c = new SqlConnection();
c.DieYouGravySuckingPigDog();
c = null; // Blast...
}
}
Use an extension method, almost...
Tom Kirby-Green on January 15, 2009 5:49 AM>> sqlConnection.Close();
>> sqlConnection.Dispose();
>> sqlConnection = null;
>> That is one hellaciously closed database connection.
You can avoid this by using 'using' :
using (SqlConnection conn = new SqlConnection("Server=localhost; Database=myDatabase;"))
{
SqlCommand command = conn.CreateCommand();
command.CommandType = CommandType.Text;
command.CommandText = "SELECT myField FROM myTable";
conn.Open();
int i = (int) command.ExecuteScalar();
}
Or for that matter use C++/CLI where you can declare your reference type 'on the stack' (at least from a syntax perspective) and the compiler will automatically do the deterministic call to Dispose when you leave the scope.
Tom Kirby-Green on January 15, 2009 5:51 AMTrue, you can use things like extension methods and/or wrapper classes for things like this (for example, we've a connection wrapper here that, along with it's other uses, has it's own 'open/close' which performs the close/dispose/null on it's own...
But there are many objects or classes like that which have a Close() a Dispose() and then, well, there's the good ole =null. Not having to do that in the first place is what I think the point of the article was at...?
Mal on January 15, 2009 5:57 AMI'm primarily a PHP programmer and I find myself calling the unset() function regularly and this is entirely down to being a little obsessive about resources. A typical page load lasts less than a second and with the amount of servers our company has we're nowhere near capacity, but for some unknown reasons in my subconscious; I still feel the need to keep the memory usage as low as possible.
Explicitly setting sqlConnection to null is a legacy of the classic VB/COM days when these things were reference counted.
I thought that carrying it over into .NET and other programming languages with modern generational garbage collectors is more a case of ignorance and/or cargo cult programming than anything else.
James McKay on January 15, 2009 6:05 AMWhat? Jeff learned C?!
Aviv Ben-Yosef on January 15, 2009 6:05 AMThis is an area where using dependency injection shines, as you can easily do deterministic disposal of all instances requested from your container.
Autofac in particular handles this scenario very well:
using (var inner = container.CreateInnerContainer())
{
var component = inner.Resolve<SomeComponent>();
// component, and any of its disposable dependencies, will
// be disposed of when the using block completes
}
Ronald: I think you completely missed the point of what was being said. Of course you can use "using" to avoid that. The point is to show how overzealous programmers get when trying to clean up resources, when it really isn't necessary.
Charles Boyung on January 15, 2009 6:16 AM
Very funny.
However, I would recommend just using a database connection pool.
In Java-land (where I spend most of my time) you can even go further. After setting the variable to null you can call System.gc() which forces the garbage collector to run Right Now.
Aviv, no of course he didn't learn C. He would definitely write a blog entry so we could all know to stop chastising him about it.
I find it kinda funny that "Double b1;" is Jeff's example of "newing something up." Besides the lack of the word "new", aren't value-types stored on the stack in C# anyway?
Ryan Fox on January 15, 2009 6:19 AMFrom now on I'm putting the following code in all my programs...
//Holy crap!, that's one hellaciously
//closed database connection
sqlConnection.Close();
sqlConnection.Dispose();
sqlConnection = null;
In general, any object having both Close & Dispose, you're ok calling only one.
It's true dispose shouldn't be required to be called, but some API's (*cough* AutoCAD *cough*) get all sorts of pissed off if things get disposed/finalized in the wrong order.
Wyatt on January 15, 2009 6:27 AMThere is always a give and take, but "overzealous" or not, it's never a bad thing to think in terms of efficiency.
While I wouldn't go so far as to set the object as null, I will indeed dispose of (or use a using block**) everything I can when I'm done with it, I think it's just prudent to not ignore such a small and easily implemented optimization.
Maybe it's just that my co-worker has been beating ultra optimization into my head for over a year now though, he is very zealous about optimizing his code, but it makes sense to go ahead and dispose of it....now if only I had a GC to take my trash to the dumpster for me....
Mat on January 15, 2009 6:32 AMContrary to what you seem to think, it is NOT necessary to use malloc() for every variable in C. It's also not necessary to use malloc() if you need to call a function that takes a pointer. It's much easier to use & (address-of operator). The only time I can think of that you need to use malloc() is when you don't want the variable to be automatically deleted when the function ends.
But I agree that languages that allocate everything with the new operator, like Java, need a garbage collector. If they didn't have a garbage collector, people would use a language that allocated most things on the stack, like C and C++.
Nate on January 15, 2009 6:32 AM@Sarel Botha: I'm fairly sure the System.gc() call in Java merely suggests that the VM do garbage collection immediately. I don't think the programmer can really force it to run at all.
JG on January 15, 2009 6:36 AMThis has got to be one of the strangest posts I've read from you so far.
I'm amazed that anyone who's been using .NET for more than a few weeks would have yet to assimilate this information, but here it is:
1. Setting a reference to null accomplishes precisely nothing. If you think it does, go back to VB.
2. The Dispose method, by contract, is supposed to clean up ALL unmanaged resources. That includes closing a database connection.
3. Considering that you have to Open() a SqlConnection before you can use it, it ought to be pretty obvious what state it's in after calling Close() - the same state it was in before you called Open().
4. No other methods should ever be called on an object after calling Dispose. Not even the finalizer - that's why you see GC.SuppressFinalize(this) in the implementation.
5. If an object supports IDisposable, it means that it owns unmanaged resources either directly or indirectly, and you need to dispose it as soon as you're done with it. Best is to use the "using" keyword so you don't accidentally forget. Do NOT simply let these fall out scope. Garbage collection obviously prevents permanent and infinite resource leaks as long as the finalizers are implemented correctly, but even a temporary leak for resources like handles and GDI objects can be a Very Bad Thing.
Clear?
Aaron G on January 15, 2009 6:39 AM@Nate: Sometimes, your object is too large to live on the stack, so you have to put it on the heap, regardless of how long it will live.
Ryan Fox on January 15, 2009 6:39 AMWhat C programmer would put the explicit cast in the malloc call??? The two lines don't mean the same thing either.
It should be:
double* b1 = malloc(m*sizeof(double));
if(b1 == NULL) exit(1); /* it just seems wrong to leave this out even in a demo! */
vs
Double[] b1 = new Double[m];
Of course the real point here is disposing of resources, which can get ugly.
free(b1);
b1 = NULL; /* optional */
vs
nothing!
(And it gets really fun when you consider error handling.)
But that isn't necessarily garbage collector; C++ does the same with RAII:
std::vector<double> b1;
[...]
// it cleans itself up!
"I'd wager the majority of programmers alive today have never once worried about malloc()." - And you'd be very, very wrong. Anyone who has ever used C, C++ or relatives, or has written an interface to a C library from a higher-level language, has dealt with explicit memory allocation.
It would be very depressing to find that that doesn't constitute the majority of programmers...
Robert Synnott on January 15, 2009 6:44 AM
Hopefully someone with more knowledge than Jeff and more time than myself will explain why it is better to explicitly dispose certain resources rather than leaving it up to the GC.
@JG: It's probably an implementation-dependent things, but in a lot of high-level languages you can indeed force a GC immediately.
Robert Synnott on January 15, 2009 6:45 AMsqlConnection.Close();
sqlConnection.Dispose();
sqlConnection = null;
You forgot to put this in a try / finally block.
Eric on January 15, 2009 6:49 AMJust recently, I saw this in an application I was working on:
try
{
cmm.ExecuteNonQuery();
}
catch (Exception ex)
{
conn.Close();
conn.Dispose();
Application.Exit();
return;
}
"The garbage collector will cruise by periodically, and when he sees stuff you're not using any more, he'll clean up behind you and deal with all that nasty pointer and memory allocation stuff on your behalf. It's totally automatic."
The garbage collector is male?! How come it's not female? ;)
Mehrdad on January 15, 2009 6:56 AMFrom MSDN:
GC.Collect()
Forces garbage collection of all generations.
This is a .Net Framework call - C#, VB.Net, etc...
And on rare occasions I've had to use it.
Nchantim on January 15, 2009 6:57 AMI used malloc once or twice...but what about c++'s new and delete? That's what I remember the most from my early days of programming.
Joe Beam on January 15, 2009 6:58 AMIn C# the easy way is
using(var sqlConnection=Connect()) {
... use the connection ...
}
this causes Dispose to be called on the connection when you leave the block: doesn't matter if you fall out the bottom, leave via return or continue or throw.
My own preference is to use "Nuke". Shorter but just as passionate. And have you ever noticed that no one deletes a folder? They "blow it away". Too many hours playing Doom I suppose.
Mike on January 15, 2009 7:02 AM>but what about c++'s new and delete?
C++ has had deterministic allocation and disposal of memory and other resources via autoptr for years, I don't think I used explicit allocation for most of my 96-2003 C++ career.
Ryan Roberts on January 15, 2009 7:03 AMThough the modern-day Garbage Collectors claim to be smart and efficient, their non-deterministic behavior pisses me off.
After all, in JAVA, gc() is a mere suggestion to the JVM :p
I still love my free(x) and delete x;
Mohit Nanda on January 15, 2009 7:03 AM"I'd wager the majority of programmers alive today have never once worried about malloc()"
Oh... I'd better check if my ~35 yo friends are still around, then.
You must have a pretty low life expectancy in the US :) :)
Wojtek on January 15, 2009 7:04 AM@Aaron
I beg to differ on some of your points.
1. Setting a reference to null accomplishes precisely nothing.
consider this:
using (var c = new Disposable())
{
c.DoSomething();
}
c = null;
DoSomethingElse();
Disposing c will release any unmanaged resources it will use, but not all managed resources. The class itself takes some memory and it may contain references to other classes. Maybe c contains a reference to several Gb of XML. By setting it to null this allows it to be garbage collected before DoSomethingElse is called, which could make an enormous difference. The CLR should perform liveness analysis on c and eliminate the need to set it to null, but if this was a field then it can't do this.
5. If an object supports IDisposable, it means that it owns unmanaged resources either directly or indirectly.
This is wrong. Take IEnumerator<T>. This derives from IDisposable. Just because something can be enumerated over does not mean that enumerating it will require unmanaged resources!
Oliver Hallam on January 15, 2009 7:08 AMNow come on Jeff I have just had to read through all the posts, to confirm you don't need sqlConnection = null;. Seems so, hopefully correct as I have never used that line.
pete on January 15, 2009 7:10 AMAssociated with this topic is that people should be reminded that they technically should still call the 'close' function on an object (as well as dispose).. technically the disposable contract doesn't state anything regarding ensuring closing of the object.. whilst most dispose methods do, they are specified not to throw an exception if the object is already closed / disposed..
so you should always do
using(var disposable = .. create disposable ..)
{
.. do your thing ..
disposable.Close(); // <-- Or whatever the 'close' method of the type is.
}
For me explicit disposal is probably most important when working on file streams.
Stephen Taylor on January 15, 2009 7:11 AMA long time ago, malloc() used to return char *, so casting was necessary. In 1989 the void * pointer type was introduced in ANSI C, and since then, malloc() returns void *.
So don't forget to include stdlib.h and then it doesn't matter if you cast the return of malloc() or not.
But if you do cast it, be aware that it will probably prevent the compiler from warning you "assignment from incompatible pointer type", as it will assume that you know what you are doing.
Thus, the recommended usage is generally:
#include <stdlib.h>
double *b1;
b1 = malloc(m * sizeof(double));
This is even better, as you don't have to worry if the type of the pointer changes:
#include <stdlib.h>
double *b1;
b1 = malloc(m * sizeof *b1);
Note that casting the return of malloc() is required in C++ however.
What's that sound I hear? Is it the sound of Comp Sci grads who learned C/C++ in school gathering their pitchforks and torches?
Vance on January 15, 2009 7:12 AM@Oliver: In your example (which won't even compile) the variable C goes out of scope when the "using" block ends, freeing itself for garbage collection.
mq on January 15, 2009 7:14 AMJeff, you still have to alloc the bl variable:
Double bl = new Double();
@mq
I added that using as an afterthought. Please pretend the first two lines read:
var c = new Class()
using (c)
My point then still stands.
Oliver Hallam on January 15, 2009 7:21 AMI don't like the "oh let the computer deal with it" attitude. This doesn't mean I like messing around with pointers either, but there you go.
I like the C++ RIAA model -- allocate containers on the stack if you
like, knowing full-well that their constructor/destructor semantics
will keep things tidy.
The finest GC experience I've ever had is with the Qt framework. I wouldn't want to develop a graphical application with anything else. You can write statemets such as
(new SomeQtDerivedWindow())->foo();
SomeQtDerivedWindow bar;
and trust that both will delete themselves when it's time.
I strongly dislike GC being a part of the language a priori, C++ has a no overhead policy for unused features and that's the way it should be. The geniuses who wrote Qt designed a memory management system THAT WORKS IN THAT CONTEXT.
Other types of program might require different memory ownership rules. I despise Java's lack of a predictable out-of-scope destructor -- do people not realise that the lifetime of a variable has meaning in and of itself?
The database example is a good RIAA example. Allocate the object on
the stack and let its destructor close the connection. The object should therefore go out of scope as soon as possible to be efficient, but ISN'T THAT WHAT YOU WANT TO DO ANYWAY? Isn't it good design to not have extraneous variables remaining defined beyond their use?
We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. - Knuth
julio on January 15, 2009 7:31 AMMehrdad: The garbage collector is male?! How come it's not female?
Because then the garbage collector would get to keep half your stuff.
The problem is that we keep repeating our resource handling code all over the place and mixing it with business logic, queries and mostly everything under the sun.
As a programmer, sometimes the very best medicine is a few passionate methods or messages in code. I wrote a TCP and UDP based service that was terminated by sending a "MurderDeathKill" string as a request. It always felt good.
Blake Schwendiman on January 15, 2009 7:40 AMb1 = (double *)malloc(m*sizeof(double));
Casting the return value of malloc (in C) is not required and hides faults.
foo on January 15, 2009 7:43 AM@pete
It sounds like whoever wrote that code has done too much VB programming before .Net. At any rate, I think that line of code is meant to be an exaggeration.
Jason Baker on January 15, 2009 7:44 AMit's good practice to clean up after yourself. anything less, is a sure mark of a jackass.
ep10 on January 15, 2009 7:45 AMI actually like the extension method. =)
I agree, however, that generally, people don't know how to dispose properly of resources that need disposing. I find it to be one of a number of misconceptions around disposing of resources in general that many programmers face.
Add to that the fact that the guideline for implementing IDispose is wrong in some ways (IMO, I lay out my case here http://www.caspershouse.com/post/A-Better-Implementation-Pattern-for-IDisposable.aspx) and it makes for a total crap-fest.
Basically, it comes to this (this is all in relation to .NET):
- Always call Dispose on IDisposable implementations when done (even if you know the implementation doesn't require it, it's an implementation detail, so yes, MemoryStreams have Dispose called on them).
- Don't bother to set references to null, in release mode (in .NET), you actually increase the lifespan of the reference.
- Call Close when you want to reopen a resource, Dispose when the resource is no longer needed.
- Throw an exception in the finalizer for objects that implement IDispose (see blog entry for more details).
Oh, and your analogy between the managed and unmanaged code equivalent is incorrect, as you can easily declare a double on the stack in C/C++.
Nicholas Paldino on January 15, 2009 7:55 AMLetting the computer "deal with it for you" might work sometimes, but it makes programmers 'soft.' If you don't understand the intricacies of memory management, pointer manipulation and cleaning up after yourself, you will NEVER BE A STELLAR PROGRAMMER.
I'm sure people can provide a counter-example to the above. Like, "oh, I don't know how memory allocation works but I can write a Perl module that proves that creationism is false," but I say bollocks to that -- and I'm not even British. You might be able to do that, but think of how much better you'd be at it if you REALLY understood what was going on.
You can't be a really good programmer without having a solid grasp of what's really going on under the covers. I'm not suggesting that you understand how the electrons flow, but I am suggesting that you form a really solid understanding of how memory works or about bitwise operations.
Garbage collection has coddled us. We've gotten very hand-wavey about things. "Oh, let the GC handle it" is not good enough. Sure, you might be able to write some decent software and never worry about it, but you SHOULD know about it. You should understand your craft. You should understand that for certain applications, a garbage collected language will NEVER DO. You should be flexible enough to read parts of the Linux kernel (as an example) and grasp what's going on.
Understanding malloc and free -- and understanding what happens when you call those functions -- will never hurt you. Knowing a language that won't patronize or coddle you will NEVER hurt you. And now I'm going to end with a sweeping generalization that will scandalize the masses and begin an epic flamewar:
If you grok C, you'll rock harder than anyone else that doesn't - no matter what language you're currently working in.
Free Memory on January 15, 2009 8:04 AMJeff,
You quoted Aristotle on this blog, you know the quote about success being a habit that has to be built. Cleaning up after yourself builds a habit of not only writing rock-solid code, but it also builds a habit of not using more memory than needed for any given task.
GC is definitely useful but it is non-deterministic. Just think of all pointless discussions about Dispose and Finalize in .NET. What is Finalize good for? Introducing a delay before cleaning up an object that is no longer needed?
Coding without knowing how to manage memory (and other resources) is exactly what lies at the root of the 25 programming mistakes you wrote the other day.
BugFree on January 15, 2009 8:16 AM@Oliver: You're right, it would do something then. However, the intention of the "using" statement is to create blocks like your first example that help do it automatically with scope. If C needs to be disposed when you're done with it, there's no reason not to declare it in the "using" block header.
mq on January 15, 2009 8:17 AManonymous: malloc() returns void*. In C, assigning this to another type (technically the void type isn't really compatible with other types) this will result in a compiler warning. It's an error in C++.
reed on January 15, 2009 8:25 AMI think I'd like sqlConn.TakeOutBackAndShoot() better than sqlConn.DieYouGravySuckingPigDog()
But maybe that's just me...
Telos on January 15, 2009 8:35 AMBrad Abrams at Microsoft once answered this question for me, via Scott Guthrie:
"Yes, it is a bit confusing…. Dispose() is implemented because SqlConnection implements IDisposable such that it works correctly in C#’s using statement. We also added Close because there is such a strong prior art to using that name. But these methods are suppose to do the exact same thing.
See the guidelines here:
http://msdn2.microsoft.com/en-us/library/b1yfkh5e.aspx"
HardCode on January 15, 2009 8:48 AMJeff - you're wrong on this one. GC is indeed the One True Way, but garbage collectors manage memory, not resources. Resources still need to be disposed of.
A correct implementation of a garbage collection on a machine with infinite memory would be to never free anything. That approach does not apply to resources: there are only so many TCP ports, files on disk need to be unlocked so the user can reopen them (possibly in a different program), etc.
Moral of the story: if you've got objects that implement IDisposable, call Dispose() on them when you're done. Pretend finalizers don't exist: the garbage collector doesn't know the "weight" of your resource, and is only invoked when it's under *memory* pressure, not arbitrary resource pressure, for any particular value of "resource".
Barry Kelly on January 15, 2009 9:08 AM"I'd wager the majority of programmers alive today have never once worried about malloc()."
That is a double-edged sword. In the one hand, it is good because of all that bull you just said.
On the other hand, it leads to a complete unawareness of what you are actually doing when you create some system-hogging resource. Many young programmers today write programs as if memory is infinite, and I think the primary reason they do that is because they don't understand that creating an object actually does anything. It may be fine to do that in business where there's a possibility of throwing more hardware at it, but it is a true coding horror in consumer applications, where it may be impossible to do that. Lest we forget - "Word 6"
Jasmine on January 15, 2009 9:10 AMWow, what a lame site.
When it comes to non-trivial applications, well written C/C++ is still an order of magnitude better (as in faster execution and easier to understand) than well written C#/Python/Java/Ruby/etc. Bad programmers (*cough* JWZ *cough*) who don't understand how to use the tools they are provided will produce bad code no matter what the language. This post is an excellent example of why you should not take what you read on the internet by self-styled "experts" at face value. Hint to the clueless: even in plain 30 year old ANSI C, you can declare a double that will automatically "clean itself up" when you are finished with it. If you are as l33t as you think you are, you should be able to figure out how fairly quickly.
Sad Coder on January 15, 2009 9:12 AMPersonally, I rather like pure reference-counted garbage collection systems like Perl uses. Once the object's reference count hits zero, it is immediately destroyed, no waiting around for the GC to do its thing while your program leaks handles or whatever. Granted, you have to be more careful when dealing with circular references, but in practice (at least for me) that doesn't tend to come up as often.
Jim on January 15, 2009 9:16 AMJeff,
Great post! I love the humor in this one, I have to say that I have once in a while "taken it out back and shot it" with SQL connections. Cause the mistrusting, insecure, inner dev-child in me cannot let GC have all the fun...I commonly refer to it as my "Office Space Fax Machine Field Basher" method, or in code overload terms myObject.OSFMFB();
Thanks again for the great post!
TexasCoder on January 15, 2009 9:22 AMYou're not disposing your objects? That's a really bad idea, Jeff, I've seen that approach in action on a high-volume ecommerce site. It's not pretty.
But why take my word for it? If StackOverflow is taking this approach, I think you'll see it soon enough (if you haven't already).
dave on January 15, 2009 9:24 AMMost of the Java people I've seen use calls to System.gc() usually didn't have a faint clue what was the cause of their problems. Calling gc was, to them, a wave of the magical Java code-healing wand.
For more info on proper nulling out of objects, see: Effective Java 2nd Edition by Joshua Bloch, Item 6 : Eliminate obsolete object references
"Nulling out object references should be the exception rather than the norm", but is sometimes necessary.
Cheers,
e
@Sad Coder:
void super_magic_self_cleaning_double()
{
double d = 0;
// do stuff.
}
:)
Tongue-in-cheek, of course, but locally scoped variables created on the stack get "cleaned up" after you leave the scope they're created in. :)
Free Memory on January 15, 2009 9:26 AMThe double closing of the connection is silly but setting objects to null after using them is generally good practice. I have seen quite a few memory leaks disappear after doing so.
Practicality on January 15, 2009 9:42 AMAll of which just goes to show that memory is *only one kind of resource*, and that accordingly GC is no magic fairie dust.
And topical, as I have just today been jumping through those stupid hoops that Java makes you go through to tidy up database connections, prepared statements and the like. Grrrr.
Pig dog? PIG DOG??
Charles on January 15, 2009 9:49 AM@Sad Coder
I'm hard pressed to find a reason to use a low level language to enhance the speed of my financial app to the point it would make an iota of difference to the end user. C easier to read and understand than C#. In many cases of code that I've reviewed, open source and in practice I find this hard to beleive.
At what point will it become acceptable to use the proper tool for the job you are doing without religious brow beating.
osp70 on January 15, 2009 9:58 AMJeff, I'm surprised to find out that you are confused about IDisposable.
I suggest you read the comment "Aaron G on January 15, 2009 06:39 AM" to learn the must-know basics.
@Aaron G: setting a reference to null makes the object eligible for garbage collection.
vitule on January 15, 2009 9:58 AMOh bravo for citing Steve Martin. I loaded this page an hour and a half ago but didn't read it. In the interim my subconscious churned up the Steve Martin bit and I wondered for a while if that was the source, and if I should comment on it. Then I wondered if Steve Martin was really the source (I was exercising, the mind wonders...)
Then I read the comments and you did the name-check for me! Well done!
I may have an unhealthy fascination w/ Martin's old albums. I once sought out Darby Conley's email (the guy who write the comic strip "Get Fuzzy") to congratulate him on his beautiful inclusion of "may I mambo dog-face through the banana patch" in a strip. Never heard back oddly =)
jj33 on January 15, 2009 10:04 AMsqlConnection.Close();
sqlConnection.Dispose();
sqlConnection = null;
All that code example shows is the dev didn't RTFM. The MSDN is pretty clear on how to use the SqlConnection correctly.
"If the SqlConnection goes out of scope, it won't be closed. Therefore, you must explicitly close the connection by calling Close or Dispose. Close and Dispose are functionally equivalent. If the connection pooling value Pooling is set to true or yes, the underlying connection is returned back to the connection pool. On the other hand, if Pooling is set to false or no, the underlying connection to the server is actually closed."
And the code example is even better showing the class in a using statement. Really if a dev can't be skillful enough to find out how to use a class properly they shouldn't be writing code.
monkey on January 15, 2009 10:05 AMAaron G is right, as is Ronald with his good advice on the using clause. If a class implements IDisposable, it is YOUR responsibility to dispose of it. Using() does this for you, as does a good IoC container (as Ryan Roberts points out).
The GC is all about MEMORY management. The IDisposable pattern is about RESOURCE management (in particular, unmanaged resources).
See
http://www.bluebytesoftware.com/blog/PermaLink.aspx?guid=88e62cdf-5919-4ac7-bc33-20c06ae539ae
and
http://msdn.microsoft.com/en-us/magazine/cc163392.aspx?ppud=4
And be sure to buy a copy of Framework Design Guidelines (Cwalina and Abrams). It discusses how to implement the Dispose Pattern correctly in detail.
I love your blog, Jeff, but you're off on this one.
Bill Sorensen on January 15, 2009 10:06 AMC:
b1 = (double *)malloc(m*sizeof(double));
C:
double b1;
Dave Parker on January 15, 2009 10:19 AMMore developers need to take some time to read the following chapter: Improved Managed Code Performance
http://msdn.microsoft.com/en-us/library/ms998547.aspx
It would clear a lot of the misconceptions up -- Dispose pattern, when to explicitly set something to null, etc.
Too much good and bad advice being passed around here. Problem is too many people can't tell the difference.
James on January 15, 2009 10:21 AM+1 Ronald... beat me to it.
Steve-O on January 15, 2009 10:26 AM>> Pretty awesome, right? I'd wager the majority of programmers alive today have never once worried about malloc(). I call this progress..
I call it an excuse to write even-more-comupting-power-sucking-applications.
Lets make bigger and havier apps - people will buy more RAM and faster CPUs. Hey, the last time I checked the price of the computer memory it was not as its price in gold. Coder time spent is more precious than performance of the coded product.
Why bother learning malloc and pointers - there is a garbage collector to *collect* the *garbage* left behind the coder.
"Release the resource as soon as I'm done" is such an important
idiom that Python has built it into the language twice. First
was the try/finally block.
f = open(file)
try:
...
finally:
f.close()
Then starting in 2.5, we have the with statement.
with open(file) as f:
...
I would think that if you needed the resources returned you would want to be able to tell the GC code to go ahead and run and make sure you deal with the resource ASAP:
sqlConnection.Close;
GC.Collect;
Casting the return value of malloc in C works unless:
1) You forget to #include <stdlib.h> or another header that declares malloc (malloc.h comes to mind for some reason)
2) The size of an integer on your architecture is different from the size of a pointer. Specifically, if sizeof(int) != sizeof(void*).
The reason both of these conditions are necessary is that functions in C that are used but not declared are implicitly declared to return int. This is not a problem on your run of the mill 32-bit intel processor, but there are architectures where the code will cause problems that are difficult to diagnose.
You may note that the posters disgruntled about the cast are likely the ones who cast thrown-away return values to void (so lint doesn't complain) and compile using -ansi -pedantic -Wall -Werror just to prove they're better than you.
Peter on January 15, 2009 11:21 AMI should probably also mention that while garbage collection works for general applications, it's no magic bullet. Try using it in real-time systems, and you will surely encounter some problems. "Why aren't your brakes working?!" -- "Oh, it's probably just the garbage collector running again... just give them a minute."
Peter on January 15, 2009 11:36 AMAh, the real bugaboo in your analysis is ole IDisposable, if you recall the days of internet wars circa pre-v1.0 .NET beta. ;-)
Was an entertaining article all he same.
Chris Gallucci on January 15, 2009 11:41 AMJeff, I know you don't know C and I certainly don't see anything wrong with that, but if you are going to write about it at least bother to do it with some rigor. Your C example is for allocating a memory block on the heap capable of holding m double values. Your C# example is for declaring Double value on the stack. So you are comparing an array on the heap vs. a value on the stack. You couldn't have mixed up your examples more than that.
Here's an example of a value on the stack in C:
double b1;
And since this is on the stack there is nothing to clean up afterward.
Here's an example of an array of m doubles in C#:
Double[] b1 = new Double[m];
Obviously, C programmer has to use free() to clean up after the allocation, C# programmer can relax about all that due to GC.
Ivan on January 15, 2009 11:52 AMC++ destructors beat GC any day.
Nicolas on January 15, 2009 12:05 PMI think that not enough attention is paid to this concept (granted that in most situations it is not that serious) and I think that is is due partly to the fact that many times it does not show up until some stress testing is done.
The example I give is an application that we have did multiple complex multi step "runs". When each run started, lots of things were allocated, during the run things got bigger and more things allocated and at the end of the run not everything got released. This was mostly hidden because the memory usage went up and down a lot during a run (20 MB - 100 MB).
Trying to find out which object was hanging around was the problem. Once we figured out which object was not getting released, it was easy to figure out why (the object genereated events and there was still one listener subscribed).
Mark Chilcott on January 15, 2009 12:06 PMRelying solely on GC in .NET is not always be the best choice.
Also, do not confuse the example in this post (Connections) with disposing of data structures. Closing a connection immediately returns it to the pool; Disposing and setting it to Null to regain a few bytes perhaps not quite so important.
Immediately recapturing data from "sizable" structures is probably wise. The GC has been proven to be laxidasical on occasion.
Always use using...
If speed is a real concern though... you don't have to destroy objects, reuse them!
Kris on January 15, 2009 12:22 PMGC only solves half of the memory management issues. You still need to be careful or you will cause memory leaks from dangling references. In a complex application they can be really difficult to track down.
Kevin on January 15, 2009 12:25 PMThe real problem in your example of the close/dispose/null is the developer simply not understanding the dispose pattern in .net. .Close() and .Dispose() do the same thing (Dispose() actually calls Close()). Setting the variable to NULL doesn't really do anything.
Robert C Barth on January 15, 2009 12:32 PM"I'd wager the majority of programmers alive today have never once worried about malloc(). I call this progress."
I would disagree. I am totally in favor of using high-level languages, and the widespread acceptance of automated memory management is a good thing. However, the fact that the majority of programmers alive today have never done any low-level programming means that they really have no idea how a computer works. This is not progress.
Kristopher Johnson on January 15, 2009 12:44 PMI'd like to take this time to promote my debug "Disposable" C# class:
http://www.cpeterso.com/gems/IDisposable.html
My Disposable class contains debug code to find common IDisposable bugs, such as "leaked" objects that have not been disposed and classes whose override void Dispose(bool disposing) forgot to call base.Dispose(disposing).
You can copy and paste my Disposable class as a boilerplate for your classes that implement IDisposable.
IDisposable is contagious: if a class has any IDisposable member variables, it should be IDisposable and dispose of its member variables. This abstraction leak can require some unexpected classes to be IDisposable because some of their private member variables are IDisposable.
Better yet, if your class does not derive from any base classes, you can derive from my Disposable class. Your class just needs to override void Dispose(bool disposing), cleanup your managed resources (i.e. dispose of your IDisposable member variables), and call base.Dispose(disposing).
If your class (like 99% of all classes) does not contain any unmanaged resources, there is no reason to define a finalizer. There is nothing your finalizer can do! Your finalizer is not allowed to touch any (managed code) member variables because (surprisingly) they might have already been finalized. You can't even call Dispose() on your IDisposable member variables.
Chris Peterson on January 15, 2009 12:45 PMExcept for the title, I don't see the point of this blog entry.
1/2 the programmers alive have never used malloc()? What a rubbish statement to make. I seriously hope that nearly 100% of programmers have used C or C++ at least once in their life.
Although I no longer program in C, knowing C means I know how my computer works.
IDisposable is for the necessary case of having deterministic disposal, and is a compiler supported feature to make simple cases quite simple (via the using keyword). It's somewhat trivial to use, and not so trivial to implement correctly, but nobody said everything was easy.
As much as you may wish otherwise, your computer doesn't have unlimited resources. You don't need to be a nazi about it, but you do need to be cognizant of it.
fyi @punters
- you should never call GC.Collect() -> I'm not even going to go into this one.
- .net does have pooled connections (configured by the provier). That still doesn't mean you don't release a limited resource as quickly as possible to have an efficient pool.
I love having a gc, because I focus on the program and not low level malloc()'s
@AL
you do not call GC.Collect() after calling sqlConnection.Close(). Close() releases any of the unmanaged resources and (probably) informs the runtime that the class no longer needs finalizing. All that is left can be reclaimed by the GC in it's normal course of duty.
Blog comments are really not the place to go through .Net memory management, IDisposable, and finalizers.
Mike Hunt on January 15, 2009 12:58 PMC- Not your best work.
ProfessorTom on January 15, 2009 1:05 PM@Oliver:
"c = null;
DoSomethingElse();
...By setting it to null this allows it to be garbage collected before DoSomethingElse is called..."
That is wrong, you should read your learning .Net book again, the GC does not run immediately and the GC does not destroys the objects right away, first marks the objects and then destroys them, and besides that the GC only starts working when the free memory is getting low, you need to add this to your code to do what you think
GC.Collect();
GC.WaitForPendingFinalizers();
Which is a bad idea, setting a variable to null in .Net its just being ignorant of how the Garbage Collector works.
Ugh, you act as if using the heap is a chore. Using the heap can be easy, especially if you properly implement it using RAII (Resource Acquisition Is Initialization)
LavosPhoenix on January 15, 2009 1:49 PMThere are many things wrong with this post.
Joe Chung on January 15, 2009 1:56 PM@James:"It would clear a lot of the misconceptions up -- Dispose pattern, when to explicitly set something to null, etc."
The point is that setting something to null is not necessary, from the page you linked http://msdn.microsoft.com/en-us/library/ms998547.aspx :
Garbage Collection Explained point 3
"The object dies due to all its references either being explicitly set to null or else going out of scope."
going out of scope is the key, if you don't set something to null the object will be collected even if its not set to null.
Set Unneeded Member Variables to Null Before Making Long-Running Calls
"Before you block on a long-running call, you should explicitly set any unneeded member variables to null before making the call so they can be collected. This is demonstrated in the following code fragment."
class MyClass{
private string str1;
private string str2;
void DoSomeProcessing(…){
str1= GetResult(…);
str2= GetOtherResult(…);
}
void MakeDBCall(…){
PrepareForDBCall(str1,str2);
str1=null;
str2=null;
// Make a database (long running) call
}
}
That is bad code because is not well factored, first, why do you have "unneeded member variables"?, if they are not needed after some point those variables don't belong to that method, so that class can become something like this:
class MyClass{
void Init(…){
string str1= GetResult(…);
string str2= GetOtherResult(…);
PrepareForDBCall(str1,str2);
}
void MakeDBCall(…){
Init()
// Make a database (long running) call
}
}
the point is that in the first example null was necessary because of a bad design, but that does not mean that you need it in order to mark a variable to be collected.
Juan Zamudio
Juan Zamudio on January 15, 2009 2:09 PMI suggest you spend more time programming and less time making a fool out of yourself. The world would be a better place.
I mean what the fuck does this:
b1 = (double *)malloc(m*sizeof(double));
have to do with this:
Double b1
besides using the same variable name?
And letting the GC close sockets? Dear God, please save me from any software written by Jeff Atwood or any of his hangarounds, Amen.
Adde on January 15, 2009 2:17 PMWAIT! Wait, wait, wait, wait.... In C++, stack variables get "cleaned" automagically, heap variables have to be manually "cleaned" and that's only if you don't use auto_ptr<> or boost::smart_ptr<> (which will be in the next standard).
Now, you're telling use that GC languages are so much better yet there are dozens post above me that clearly say that you should through some hoops to have your vars cleaned up...
I'm not sure I'm getting it. To be clear:
C++ stack variable:
{
int i;
}
C++ heap variable:
{
auto_ptr<int> pi = new int;
}
***Garbage collected*** C# (from a post above):
{
using(var disposable = .. create disposable ..)
{
disposable.Close(); // Or whatever the 'close' method of the type is.
}
}
And somehow that last method is... better? Have I fallen in bizarro-land during lunch?
W on January 15, 2009 2:22 PMIf you work in scientific computing like I do, automated GC just doesn't cut it. Come all ye Optimizationers and Algorithmaniacs to the computational electromagnetics world.
chris on January 15, 2009 3:01 PM> What? Jeff learned C?!
Obviously not as others have pointed out the C example is allocating an array of doubles on the heap.
The second example is allocating a single double on the stack.
Grom on January 15, 2009 3:14 PMGarbage collection is all well and good until you get something like this: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4907801
In that bug(which according to Java devs is NOT a bug) If you create a popup on a window, a static reference to that window(and all it's associated resources) is held until you do another popup, never mind that your app only uses that window once for config, then goes to background and never shows up again. All the resources pointed to by the window are held(which could include references to huge chunks of memory) since they are reachable from the root.
What do you do here? Trust the runtime? Trust the garbage collector? Or do as the customer did and reflectively null it out?
Z on January 15, 2009 3:24 PMJust a note about Close+Dispose, in hopes that it will help some other programmer.
I see a lot of:
file.Close();
file.Dispose();
I've done it myself to "fix" some issue where a later open on the same file failed due to "file locked" issues, but the issue never completely went away, no matter how many times we closed the file. Finally, we figured it out.
file.Close() works as advertised, as does file.Dispose(). However, many virus checkers will watch for the Close() and will immediately scan the file. This locks the file until the scan is created, creating a race condition between the virus checker and the next time the program tries to open the file. Close+Dispose "works" because it is an inconsistent race condition and you got lucky.
Doug on January 15, 2009 4:04 PM@Juan: Re-read my comment. I didn't state setting variables to null is a good practice. The article, as you pointed out, clearly did not either. Also, your deconstruction of that code example does not change the point the article was making. If you have static or instance variables that are no longer needed, explicitly setting them to null before long running operations can be beneficial.
Would I use that specific example with developers I teach? Most likely never. LOL! But, if I had no choice I could make it work as pedagogical tool. For less experienced .NET developers, it would serve well to illustrate the general idea and syntax of setting non-local variables to null explicitly. While for the more knowledgeable developers it could be leverage to teach how in some cases you could avoid it altogether just as you did.
James Arendt on January 15, 2009 4:33 PMI agree wholeheartedly, but given the sophistication of this site would offer one corrolary - in certain cases a human developer will pound the stuffing out of a garbage collector, but a very specific set of conditions need to hold true.
1 - the primary stressor on memory comes from dynamic management of a large number of _homogeneous_ objects.
2 - the developer is highly skilled in techniques such as STL allocators, OS page mapping, etc.
In this specific situation, you will find that the optimal solution is to implement a highly specialized memory management system that exploits the homogeneity of the object collection, using large block allocations from the OS and demanding more proactive management (create and release) of the client software - 3D graphic engines, low level or large scale network applications and other seriously furball applications benefit -
Secondly, there's one further optimization in GC land that I didn't see called out. In general one should leave the garbage collector alone, and let it do it's thing. However, in a situation (often seen in low level 3D render loops) where
1) the iteration cycle time is slightly in excess of the Generation 0 threshold time
2) a large number of object allocations are done where the object lifetime is scoped to a single pass
then it can be beneficial to trigger the garbage collector at iteration start - the reason for this is that a generation scavenging gc assumes that if the lifetime of an object is not very short then it may be very long (this is dependent on implementation, # of generations, etc) - in certain (specific and measurable cases) you can get an aliasing problem where the lifetime of a large number of objects causes them to (incorrectly) pass these generational boundaries and be preserved for too long a time - the symptom is that memory consumption (careful how you measure, as the gc is dealing with things it thinks are in use but it hasn't actually checked) shows a distinct sawtooth pattern - swells and swells and swells, then the machine chokes up for a bit (the full scan runs, effectively a complete mark and sweep pass) and the memory footprint shrinks by an order of magnitude.
Certainly an edge case, but given the sophistication of the readers here, one they're more likely to bump into, I would guess - certainly the average enterprise developer is better off with a GC and should just leave it alone to do it's job
Mark
A value type is on the stack. It will be stack collected, just like a "int a;" would in C. The comparison is completely bogus.
Setting a reference type to null or 0 does *nothing* in .NET, except in cases where you later re-use the variable (which denotes bad programming habits). Actually, let me say that it's even worse, and could actually cause the GC to take even longer to collect the memory.
*ALWAYS ALWAYS ALWAYS* Close/Dispose unmanaged files/connections/etc. It doesn't matter if you have the biggest server in the world, the connection pool will scream uncle long before your connection does. It will kill your app, and is in no way an "optimization".
The "using" keyword is the easiest, cleanest way of dealing with IDisposable types, and should be every developers way of managing the lifetime of things like connections and files.
I chuckle a bit that Mullin above continually refers to the "sophistication" of the readers here. If they were sophisticated, Jeff, you would be shamed into unpublishing this nonsense.
Just A Guy on January 15, 2009 5:21 PM> I find it kinda funny that "Double b1;" is Jeff's example of "newing something up." Besides the lack of the word "new", aren't value-types stored on the stack in C# anyway?
> Jeff, you still have to alloc the bl variable: Double bl = new Double();
> Obviously not as others have pointed out the C example is allocating an array of doubles on the heap. The second example is allocating a single double on the stack.
I agree, it's not a perfect example -- but that's the first C code snippet I could find that used malloc().
> Double[] b1 = new Double[m];
Thank you, I made the correction.
Please forgive the poetic license. I am, truly, the world's worst C programmer.
Jeff Atwood on January 15, 2009 5:22 PM@James:
"If you have static or instance variables..."
I thought static variables were ignored by the Garbage Collector.
I think you can use anything as long as you know why (avoid Voodoo programming), I have seen people using VB.Net like VB6, parameters by reference, String to concatenate, optional parameters (instead of overloads), the list goes forever, I'm from Mexico and most people where I work don't understand English (my English is not that good either), and the documentation in Spanish is very lame and scarce, maybe that's the reason I see a lot of mistakes like that.
Why is learning C such a bad thing? This is just a weird view. Don't get it. Especially when it seems like the main point of this blog is, presumably, to promote the "love" of coding and computer programmer; as well as the promote becoming a "better programmer." I dunno. Just seems odd.
Charles on January 15, 2009 5:44 PMIf advantages of using GC were really beneficial, we would be basking in the glow of totally error-free software, without memory leaks, without buffer overruns.
Reality is, of course, very different. 90%+ of all security problems with software have been tracked to poor memory/resource handling in code.
If this isn't a proof by contradiction, I do not know what is.
GC is a nice academic concept. In the real world, it is a pain in the neck because it is non-deterministic. Yes, you can force it to run when you want it, but that's the same as cleaning up after yourself, isn't it?
BugFree on January 15, 2009 5:45 PMSo, GC is great, right? Throw cpu cycles at it - they're basically free, after all!
Yeah, do that on a 400MHz embedded ARM9. The art of mallocing and freeing is still *pretty* important for a lot of applications.
Justin on January 15, 2009 6:02 PMThere are a lot more programmers that have written C than you think, not everyone comes from a Visual Basic application writing background.
You've given us yet another implicit example of why learning C is really a necessity for becoming a well rounded programmer.
> The art of mallocing and freeing is still *pretty* important for a lot of applications.
Agree, which is why I said "it can be a pretty important optimization" -- depending on the situation, obviously.
The point of this post is the strange tension between auto-GC, which works very well 99% of the time, and the 1% of the time you actually need to care *when and how* resources are released.
Oh, and figuring out when you're in that 1%.
Jeff Atwood on January 15, 2009 7:37 PM> Oh, and figuring out when you're in that 1%.
Thats why I like doing it manually, you never really know what the garbage collector is doing, and what you don't know can be dangerous.
About the setting the pointers to null after disposing/freeing them, It can be a debugging technique:
if some code touches that pointer later, then you will get a nice clean null pointer exception. If you just leave it as it is, then its still technically pointing at valid data and the code might work, until more memory is allocated on top of it and you get corruption.
But its practically useless, because it won't cross functions unless you use globals. I recommend valgrind instead.
Scott Mansell on January 15, 2009 8:10 PMCalling Dispose is rarely about optimisation, actually - it's about correctness.
If your website hangs completely when you've got more than 10 users because all the threads are waiting for a database connection (and nothing adding memory pressure to trigger a GC), that's a correctness issue, not an optimisation issue.
If your app fails to open a file it's just written because you didn't close the stream, that's a correctness issue, not an optimisation issue.
If your app crashes because it can't allocate any more GDI+ handles, that's a correctness issue, not an optimisation issue.
Optimisation should be about making something run more efficiently, not about making it run when previously it was hanging or crashing. For limited resources other than memory, you really, really shouldn't rely on the garbage collector to release those resources - because the GC only cares about memory pressure.
(Just to clarify, I agree with the rest of the post, and you clearly understand the you need to close database connections. I'm just taking issue with your use of the word optimisation.)
Jon Skeet on January 15, 2009 11:33 PMI'm in the camp that it's not really an optimization if it helps your application function correctly 100% of the time.
Why bother avoiding the inevitable? Why skip this simple step if it will save you time and $$$ later?
It's like home owners insurance (provided you don't live in a disaster prone area). You're about 99% sure nothing bad is going to happen to the home...and yet you still own the insurance policy, right?
I could go on about the issues I've seen but let's just say that the guidance is there for a reason.
While calling Dispose() or implement the "using..." pattern might not have a noticeable impact to your application today (with <5 users) - that is not to say that it won't when traffic increases or you add additional functionality.
Greg Varveris on January 15, 2009 11:35 PMNo comments on the article, but Jeff, can you avoid linking to yourself in the quotations that you use? Yes, "Worse is Better" is hyperlinked in the original text. But not to the URL that you linked to. As it is now, it looks asi if JZ linked to your blog post of January 30, 2008 when he wrote the article on Februrary 1, 1998
Anonymous Coward on January 15, 2009 11:55 PM> asi if JZ linked to your blog post of January 30, 2008 when he wrote the article on Februrary 1, 1998
JZ is pretty awesome, but I don't think he's invented time travel.
Yet.
> Calling Dispose is rarely about optimisation, actually - it's about correctness.
My concern is that it's a short mental trip for a lot of developers from "there is this VERY NARROW set of items that I need to explicitly dispose" to "I want to manually dispose of everything!".
I worked with developers who insisted that every DataSet be explicitly disposed in Every. Single. Function.
http://www.codinghorror.com/blog/archives/000031.html
I also argue that the runtime should be smart enough to dispose resources that are IDisposable immediately once they fall out of scope. On some level, the runtime is making me do work it should be able to do. If it isn't in scope, and there are no references to it, and it's ultra-mega-system-critical, hey guess what? Go ahead and dispose of it for me.
Jeff Atwood on January 16, 2009 12:09 AMExplicitly calling dispose on a database connection in .Net causes some VERY nasty things to happen - specifically the connection pool manager gets its knickers in a knot, it never returns connections to the pool, resulting in the pool eventually becoming full and no more connections being possible.
This problem esists in .Net 1.1 and .Net 2.0, not sure about .Net 3.0
Yes, there are certain types which implement IDisposable which really *don't* need to be disposed apart from in very specific circumstances. These have usually derived from another type which implements IDisposable - particularly MarshalByValueComponent. However, I think it's a safer rule to remember those types which usually *don't* need to be disposed than those which do. I'd list: DataSet, DataTable, MemoryStream, DataContext (usually), StringReader, StringWriter.
Which other commonly used types implement IDisposable but which *don't* cause potentially serious issues (not flushing data, not closing file or network handles, releasing connections back to a pool, not releasing GDI+ handles etc) if you don't dispose them?
As for the runtime being smarter - it sounds like you're after reference counting, or some other form of deterministic finalization. It's not like MS didn't think about this. Brian Harry wrote a great post about this, but unfortunately I can't find it right now... Will look again when I've taken my eldest son to school...
Got it: http://blogs.msdn.com/brada/articles/371015.aspx
Jon Skeet on January 16, 2009 1:07 AMHaving said that it's hard/impossible do to fully automatically, I seem to remember that C++/CLI has some extra features around this which perhaps C# could have come up with first. (I'm not sure it would be a good idea to change C# at this point, though I could be persuaded.)
See http://blogs.thinktecture.com/cnagel/archive/2006/04/10/414477.aspx
Jon Skeet on January 16, 2009 1:18 AM> it sounds like you're after reference counting,
Er, no, I'm after IDisposable things getting prioritized by the GC when they fall out of scope. Nowhere did I say anything about ref counting.
Somebody call the garbageman, we've got extra-stinky trash out this week.
Jeff Atwood on January 16, 2009 1:19 AMSo do you want to force a full GC every time you come out of any scope? Bear in mind that IDisposable and the garbage collector don't know anything about each other. The GC knows about finalization, and usually a finalizer will call Dispose - but that's all.
So, do you want the compiler/runtime to insert calls to Dispose every time any disposable variable goes out of scope? If so, how do you stop it from disposing of shared resources which are still in use?
If you really want the GC to handle this, we're in trouble - because I don't want the GC to have to walk over even gen0 every time I exit a scope.
Jon Skeet on January 16, 2009 1:28 AMIntegrity.
Jeff in this artical, you quote somebody who hyperlinks "Worse is Better" by Richard Gabriel. However in your quote you change the hyperlink the "Worse is Better" by Jeff Atwood.
That is a misquote of the original and I call you.
You have a great blog, and I enjoy reading it very much, but you must have integrity when you quote somebody.
When quoting you can't change the hyperlinks they use or insert extra hyperlinks to your own work, any more than you can change the meaning of what they said by missing out the odd word here and there.
Please don't do this again.
Mat
Mat Roberts on January 16, 2009 1:41 AMThis article is wrong (and dangerously misleading). Failure to dispose IDisposable objects is a real error, and can easily cause nondeterministic crashes and deadlocks.
For example, opening a log file and writing a line and forgetting to close it means the next time you try and open+write your program might crash (file locked) - or it might not, if the file stream was garbage collected.
If you open a DB connection and fail to dispose, you may or may not run out of connection pool connections, depending on memory load and pool size. It won't be easy to reproduce, anyhow.
If you open a database streaming reader and fail to close it, all subsequent sql commands on that connection will throw a connection error - unless the reader was collected.
If you open a transaction and fail to dispose it, subsequent transaction-aware commands will jointly succeed or fail - possibly changing semantics (though almost certainly just throwing an exception, eventually).
The permissiveness of C# when it comes to IDisposable objects is perhaps its #1 design flaw. It's almost always an error if an IDisposable is not used within a "using" or an equivalent try...finally block.
Garbage collection is good; it doesn't make resource management irrelevant.
Eamon on January 16, 2009 3:07 AMAs a programmer I like to have as much about my program as possible. Garbage Collection (GC) takes a large amount of control away from me. It is very convenient to use (don't care about memory), but it means I have pretty much no control how much memory my application will use at what time (since the GC will run whenever it wants to run, dumping an object does not give me the memory of it at once - it may not even give me that memory 30 seconds later). Further every GC run takes CPU time, so I cannot know when my app will use how much CPU time to perform operations, since GC operations are performed in the background at any time the system decides to do so.
You may not care for such kind of issues in high level languages like PHP, but the kind of applications usually written in C are written in C because it is hard to make them run faster or use less memory when writing them in any other language (maybe when you write them in pure assembly, but that's it) - and here these are important aspects. A C GC is only useful if:
1. I can stop it at any time, so I can make sure it will not run within a performance critical section
2. I can force it to free up a memory block right now, because I need this memory back right now (and not any time in the future)
However a GC working like this will hardly show much advantage over manual memory management. Actual manual memory management has no real disadvantages to begin with. The only disadvantage is that you must remember to free memory at some place and at some time. And this is not really a disadvantage of the memory management itself, the disadvantage comes from the fact that human beings are imperfect and do stupid things like never freeing memory again or freeing it too early when it is still in use - so the main task of a GC is to do something for you that you could also do yourself, but for that you might be too stupid to do it right.
Regarding issues like "Yes, but it also saves me of writing more code". You are not really arguing about this tiny free statement, are you? Despite that, for throwaway objects you can write yourself convenience functions, like release pools.
int blahblah (....) {
int result;
ReleasePool p = createReleasePool();
...
...
void * memblock = automalloc(2000 * sizeof(double), p);
...
...
int * intpointer = automalloc(sizeof(int), p);
...
...
releasePool(p);
return result;
}
automalloc does a normal malloc and stores the malloc result internally into the pool p, then it returns it.
Right before you are done with a huge function, creating plenty of small temporary objects, you release the pool itself, which means it will once run over all stored pointers and call a free on them. This way you don't have to write hundreds of free at the end of the function.
Mecki on January 16, 2009 3:49 AMWhen I first read of GC I had sort of mixed feelings. I tried to like them as I tried to getting accustomed to more modern languages.
Nowadays, I am basically as I was when I started.
If I lived in a more high-level context then I suppose I would now be using GC, but I often deal with HW resources which I simply cannot afford to avoid considering till their sure death.
And I'm still scared as hell by destruction errors (which admittedly usually happen because somebody didn't its work right) which suddendly start to happen at random.
And I still hear some horror stories.
And, it sucks to say, but I have gone back to the 'binary blob' approach. Sometime, OOP and type safety don't fit it the first place, let alone GC.
I'd love to have less work to do, really. And since even the cheaper PC right now (intel graphics not included) can run everything I do super-fast, I could also consider paying even some perf.
But I think I will still wait a little before switching.
MaxDZ8 on January 16, 2009 4:14 AM> I also argue that the runtime should be smart enough to dispose resources that are IDisposable immediately once they fall out of scope. On some level, the runtime is making me do work it should be able to do. If it isn't in scope, and there are no references to it, and it's ultra-mega-system-critical, hey guess what? Go ahead and dispose of it for me.
That's called finalization. But it's expensive.
Kent Boogaart on January 16, 2009 4:33 AM"I'd wager the majority of programmers alive today have never once worried about malloc()."
From Jeff's about me page it doesn't sound like he has either: http://www.codinghorror.com/blog/archives/000021.html.
Todd on January 16, 2009 4:41 AM>Explicitly calling dispose on a database connection in .Net causes some VERY nasty things to happen - specifically the connection pool manager gets its knickers in a knot, it never returns connections to the pool, resulting in the pool eventually becoming full and no more connections being possible.
This is **complete and utter nonsense** (Honestly, junior programmers out there should be extremely wary taking anything they read online. Not only was Jeff's entry completely wrong on virtually every level, he has a squadron of followers that are just as ignorant, but they desperately hold onto their hilarious nonsense.)
It has been a best practice to *always* dispose connections since day 1, preferably using the using keyword. Millions of developers are doing it, strangely without error, and have been for years. Those morons that aren't are enjoying constant "connection pool is exhausted" errors.
"Please forgive the poetic license. I am, truly, the world's worst C programmer."
I would like to know if Jeff has ever written a C app that ran in production somewhere. For some reason, I doubt it.
Todd on January 16, 2009 5:02 AMAm I the only one here who suspects Jeff doesn't even have a solid grasp on the differences between the heap and the stack? This is a good example reason why programmers should be forced to learn C!
Kenn Bracey on January 16, 2009 6:23 AMThis article is an example of learning to know when NOT to write about something. Not understanding the subject matter well causes too much confusion.
Weebles on January 16, 2009 6:31 AMI've had a question about this on stackoverflow, of which the answer makes me think connection.close is all you can do to get rid of the resource. The gc makes up it's own mind of when to release the resources used.
http://stackoverflow.com/questions/12368/how-to-dispose-a-class-in-net
Jorrit Reedijk on January 16, 2009 6:56 AMAnother vote of agreement with Arienne and Nicolas: good C++ destructors over garbage collection any day for me. When it comes to memory/resource allocation/de-allocation, I'm a control freak. But good class libraries allow me to be a control freak without being a micro-manager as well. We all win.
Garbage Collection still feels like an abdication of basic responsibility on the part of the programmer to me. In my opinion, it is best used in the forked sections of fork-and-die-model services so that whatever unpredictability it introduces (which admittedly is much less than it was in the days of SmallTalk) will only affect one session of the system and not the system as a whole.
As for writing a SQL connection object in any object oriented language whose destruction semantics don't include a Close-if-open, Dispose-if-necessary semantics -- that reeks of kludginess and would make me curious about what other bad design choices are being made in the overall system.
Arthur Klassen on January 16, 2009 7:28 AM@Jorrit: I don't see how you reach the conclusion that connection.Close() is necessary from that question. It doesn't mention Close() methods at all.
And the non-memory resources can't be released by the GC directly, because the GC is *only* responsible for memory. It will call a finalizer for you, which lets you release non-memory resources, but that's all.
Jon Skeet on January 16, 2009 7:48 AM^ Don't argue with John Skeet
|
@HB: Don't put an h in my name either ;)
Jon Skeet on January 16, 2009 8:41 AMIn my experience, calling Dispose(), at least on a database connection object, is absolutely necessary.
When we first transitioned to .NET, we had a number of apps which would have connection problems after a short time of use. We traced the problem to the database connections *merely* being Close()ed and nulled. Apparently, that wasn't enough. After that, as a general rule, if something is IDisposable it goes in a using statement. No explicit Close, Dispose, or null... Just wave bye-bye as you leave the block.
Bryce on January 16, 2009 9:21 AMI'm a C/C++ guy so I don't know much about garbage collection but what I hear my colleagues saying is that it always kicks in at exactly the wrong time.
Bill Wixted on January 16, 2009 9:50 AMTwo of my favorite (though perhaps 15 year old) method names:
ADOBitchSlap()
DealWithThisStinkingGodRottingErrorAndTellMeIfIShouldResume()
And for the love of all things holy in the world would the people who are flat out wrong about the CLR's GC behavior (you know who you are--or at least I do) spend a little snuggle time with Maoni's blog? An object is not eligible to be collected until it's no longer rooted, additional memory is being allocated, and the size of the requested allocation exceeds the available capacity of the ephemeral allocation segment (I apologize for just now sounding a bit like a certain earlier polysyllabic-vocabulary-word-spewing 'Mr. Fancypants' poster). +1 to everyone who mentioned the difference between resource and memory pressure. -1 to everyone who posted a religious rant or got all up in Jeff's grill (you know who you are Mr. Poopy Pants!). -NaN to everyone who responds negatively to me (and cons yourself up a big fat "that's what your mom said" to go along with it).
Brian on January 16, 2009 10:17 AMJeff, you don't have to call Dispose() on every single object. In fact, you can't because it's not part of the object class. You can only dispose of objects that implement IDisposable, which is the implementer's way of saying "hey, this thing uses precious resources, you probably want to dispose of it when you're done instead of waiting for the GC to come along."
So take their advice. Call Dispose( ) on every IDisposable - yes - even DataSets in every single function.
Programming's *hard*, isn't it?
dave on January 16, 2009 10:21 AMIt's a shame to think that the *majority* of programmers never had to deal with memory management by hand. I love C# and the garbage collector, but the pain you feel in debugging memory leaks and memory management problems really teaches you to code carefully and, well, to give a shit. There's a downside to making it "easy", allowing us to become to lazy is a bad thing.
But hey, I'm not ready to give up C# and the garbage collector, but I sure am glad I learned how to write code in C and C++.
--Matt
Matt on January 16, 2009 11:17 AMIt's a shame to think that the *majority* of programmers never had to deal with memory management by hand. I love C# and the garbage collector, but the pain you feel in debugging memory leaks and memory management problems really teaches you to code carefully and, well, to give a shit. There's a downside to making it "easy", allowing us to become to lazy is a bad thing.
But hey, I'm not ready to give up C# and the garbage collector, but I sure am glad I learned how to write code in C and C++.
--Matt
Matt on January 16, 2009 11:18 AMJeff, JZ *did not* link to your blog posts, especially on "Worse is Better", where he explicitly linked to his own blog post about it, not yours!
The way you put it seems to me like you're wanting to make it look like JWZ is linking to your blog posts, which would be remarkable - but isn't true. And it scratches your reputation a little bit (at least for me).
Andr on January 16, 2009 12:17 PMAgree with Andr on the "worse is better" link falsification - that's a dirty trick that will only damage your reputation.
Kenn Bracey on January 16, 2009 5:58 PMA lot of the people pontificating about .Net GC and garbage collection don't know as much about it as they think they do, and that recursively includes a lot of the people pontificating about how the others don't understand .Net GC. I'll exempt Brian, Jon Skeet, and chris by the sound of it.
A few random points: 1) Arrays aren't value types, so where do you think all C# arrays are allocated, including big arrays? 2) If you haven't read Maoni, you probably don't understand what goes on the large object heap or how the large object heap behaves. It's not automagically reclaimed as you probably think it is. 3) Setting a local reference variable to null is useless. Failing to set a reference variable in some longer-lived object to null can be deadly. 4) Someone already mentioned this, but setting an event handler for an event is making a reference to the class which contains that event. If you don't remove event handlers for an object, it won't go out of scope.
Clifton on January 16, 2009 11:44 PMIts better to stay silent and look a fool, than to open your mouth and remove all doubt.
You call it progress, I call it ignorance.
Your ignorance is compounded by arogance. The need to explicitly call Dispose just shows that designers of C# don't really inderstand the problems with resources release. RAII is essential and with it I can have smart pointers that don't require gc and releases both memory and any other resources. Calling memory allocations in C/C++ 'hard to debug' is another sign of ignorance. Try debugging memory leaks in C#. We've spent weeks on couple of C# appplications with memory problems. C++ is a breeze to locate memory leaks. C# is a bicth.
The Dispose pattern is just plain stupid. Interestingly in C++/CLI RAII is present. Most C# users doesn't undestand any of memory issues and doesn't even know it isn't a problem in C++ (thanks to smart pointers) for years now. Many C# users feel good about themselfs thinking they use 'moderm' language and their ignorance and arogance prevents them from noticing they are ignorant and arogant.
RealProgrammer on January 17, 2009 2:34 AMRealProgrammer: Out of interest, how well do smart pointers handle circular references? I won't pretty tend be knowledgeable about C++ in general or smart pointers in particular, but I *thought* they were reference counted, which has obvious problems in some cases. (I readily acknowledge that circular references aren't needed particularly often.)
I would also say that I've very rarely found problems in C# or Java code that I've worked on in terms of memory leaks. Non-memory resource leaks are slightly more common, but not that bad.
Jon Skeet on January 17, 2009 3:56 AMJeff, that is not C code. C does not re
James on January 17, 2009 5:38 AMJeff, that is not C code. C does not require a cast of the result of malloc. C++ does. Perhaps you're checking your code with a C++ compiler.
>Arrays aren't value types, so where do you think all C# arrays are allocated
Yeah, only Jeff's post first had a simple double. After being blisteringly slapped, he changed it.
Geez on January 17, 2009 5:47 AMSeriously Jeff, you need to fix the "worse is better" link fiasco (as in restore the link from the original) and you ought to apologize to your readers and to JZ. You didn't even point out what you had done. I don't know what worries me more: If you didn't see what was wrong with what you did, or if you did see it but did it anyway...
omfg on January 17, 2009 8:20 AMman, there are a lot of comic book guys commenting on this blog
REd on January 17, 2009 8:58 AMcool. i made a comment similar to this on the previous post. :)
jheriko on January 17, 2009 5:50 PM@Jon Skeet. I didn't know .Dispose was necessary for freeing the connectionpool for instance, I thought .Close was enough for that. That's because sqlconnection uses unmanaged resources?
After reviewing our libraries I thankfully see we use dispose on all our connections, probably therefore never running into any database connection problems.
Jorrit Reedijk on January 18, 2009 6:29 AM@Jorrit: Close *is* enough, but so is Dispose. It's easier to call Dispose safely with a using statement than it is to put Close in a finally block, so it's my method of preference.
Apologies if I implied otherwise somewhere. You certainly need to *either* Close or Dispose database connections, but it doesn't matter which. (I *thought* there was a difference but the docs for SqlConnection.Close say they're functionally equivalent. It could vary for other providers, of course.)
Jon Skeet on January 18, 2009 7:45 AMThere is one very good reason that you may want to really force memory to be freed and this is when the contents of the memory are sensitive.
For instance if you have a variable containing a password or an un-encrypted credit card number that you have just encrypted (you are encrypting it aren't you?) you really must ensure that that memory is destroyed right now and be sure its contents are destroyed first.
In this situation garbage collection can be a real pain because you don't know when the memory is freed
John Mersh on January 18, 2009 1:18 PMIn some sleep-deprived, impassioned nights programming for operating systems class, my partners and I would resort to the same sorts of naming:
inode.RainDownOnFromTheFieryDepthsOfHell()
It might not have been the most efficient use of horizontal space, but I do think there's something to it. The visuals in our minds of what was happening in the internals of the operating system suddenly became a lot more exciting.
Danny on January 18, 2009 7:22 PM@John Mersh: In that case I'd suggest using System.Security.SecureString and only decrypting to char arrays which can be wiped immediately after the value is no longer needed. (SecureString doesn't have any conversion members built into it - you need to go via SecureStringToBSTR, which is a slight pain. I can see why providing a ToString would be dangerous, but CopyInto(char[]) would be nice. However, I have a vague recollection than SecureStringToBSTR returns a pointer to memory which is guaranteed not to be pages, or something like that.)
Jon Skeet on January 18, 2009 10:34 PM>>Pretty awesome, right? I'd wager the majority of programmers alive today have never once worried about malloc().
And you'd be wrong.
Bill on January 19, 2009 12:33 AM> Failure to dispose IDisposable objects is a real error, and can easily cause nondeterministic crashes and deadlocks.
Well, except if it's a DataSet, right? DataSet is IDisposable, and it really doesn't matter if you dispose it or not.
> (I *thought* there was a difference [between .Close and .Dispose] but the docs for SqlConnection.Close say they're functionally equivalent. It could vary for other providers, of course.)
Like I've been saying -- it's clear as mud.
Jeff Atwood on January 19, 2009 12:40 AM> The way you put it seems to me like you're wanting to make it look like JWZ is linking to your blog posts, which would be remarkable - but isn't true.
This idea is so patently freaking ridiculous that I hadn't even considered it, honestly.
Again -- unless JWZ has invented a time machine. Or if the reader is so incredibly dumb that they have no concept of time, or adding relevant hyperlinks to a quote.
I'll go ahead and err on the side of assuming average readers are smart enough to figure this out, hysterical commentary aside.
Jeff Atwood on January 19, 2009 12:44 AM>Well, except if it's a DataSet, right? DataSet is IDisposable, and it really doesn't matter if you dispose it or not.
I fail to see your point here. Take some time out, read Jon Skeets posts here, maybe then you will learn why you are full of bullshit.
>This idea is so patently freaking ridiculous that I hadn't even considered it, honestly.
How about you learn some respect for other peoples work. You clearly changed the "Worse is better" link from http://www.jwz.org/doc/worse-is-better.html to http://www.codinghorror.com/blog/archives/001046.html. I don’t care what you think you were doing, its downright rude! I am sure if I quoted something from you can changed some links to link to my work instead of yours you might be slightly pissed off.
>I'll go ahead and err on the side of assuming average readers are smart enough to figure this out
Some of your readers are not happy with your attitude and with some of the completely wrong information you are spreading over the web.
Jeff,
As someone else already said, you avoid the problem of "hellacious" access via the "using" statement in C#. As for setting the object to null, doing so doesn't release the object "right now" anyway - it just invalidates the reference on the stack. Remember that object references are just like pointers - whether they point to actual objects doesn't change whether the objects are mapped in memory.
Theoretically, you could: if you called -
sqlConnection = null;
- then your thread was interrupted by the GC and collected, it can (in the context of the thread) be available "immediately." But that's so chaotic that it's not really practical. As soon as the method goes out of scope the object will be ready for release anyway.
Otherwise, your memory is still being eaten by that object. You just can't access it anymore.
Cheers!
Rob Paveza on January 19, 2009 11:05 AMJeff, you surprise me! Pointers and memory management are what make programming an enjoyable challenge. Of course, I'm speaking as an embedded software engineer, which still has pitfalls that desktop programming has long since forgotten.
Jim on January 19, 2009 12:29 PM> Take some time out, read Jon Skeets posts here, maybe then you will learn why you are full of bullshit
Jon Skeet sez: "Yes, there are certain types which implement IDisposable which really *don't* need to be disposed apart from in very specific circumstances. These have usually derived from another type which implements IDisposable - particularly MarshalByValueComponent"
Amazing, because that's exactly what I was pointing out all along -- the IDisposable stuff is a) at odds with the concept of automatic garbage collection and b) confusing (close/dispose) and sometimes outright improperly implemented.
> How about you learn some respect for other peoples work.
Yeah, linking to an entire post of mine talking about the concept of "Worse is Better", which in turns links to *every single significant article on the web about it* -- how incredibly disrespectful of me!
You read it here first people. I invented "Worse is Better". Yep.
> Some of your readers are not happy with your attitude
If you make everyone happy all the time, you're not doing anything very interesting.
Jeff Atwood on January 19, 2009 7:05 PMJust to mention it: in all these cases where you really cannot avoid explicitly closing / freeing resources (like the mentioned SqlConnections, JCA Connectors etc.), there is a nice idiom in the more advanced languages that have closures that avoids all this annoying opening and closing. Consider this:
new File("results.txt").withPrintWriter {writer ->
writer.write("... interesting data ...")
}
The idea is you pass to File.withPrintWriter a closure (think of it as a function taking a FilePrintWriter). The function creates a FileWriter, calls the closure with it, and closes it afterwards. Nice and concise, isn't it?
See also http://joeygibson.com/2008/10/04/why-i-love-closures/
Hans-Peter Strr on January 19, 2009 11:38 PMJeff,
No matter what the content of your replacement link, changing a quoted link *without pointing this out AT ALL* is, whilst not the crime of the century, a pretty underhand way of trying to drive extra traffic to your old articles. We all know that's why you've done it, at least credit that you've changed someone's quote with either a style or (preferably) a note in the text. You can do this with square brackets - e.g. "Worse is Better [link modified]". Doing so is unlikely to harm your traffic but will certainly restore goodwill.
Regards,
Kenneth
Kenn Bracey on January 20, 2009 5:45 AMBoth the content of this posting and his inexplicable and ill-mannered defense of misquoting someone else tarnishes Mr. Atwood's reputation in my eyes.
Advocating that programmers not call Dispose (or use "using") and, furthermore, suggesting that they're "obsessive compulsive" for doing so is doing a disservice. Closing a file, closing a database connection (or returning it to the pool), etc., are important for correctness, not just for frugality. If the GC happens to collect your file stream before you need to open the file elsewhere, then your program functions properly. If not, it won't. This kind of bug is not always easy to diagnose. Advocating such lazy and error-prone coding is not what I expect from coding horror.
Misquoting someone and then ridiculing those who point it out (especially when they point it out in a polite and deferential manner) is, again, not what I expect from this blog. While it may be useful to have a link to Mr. Atwood's own thoughts on the matter, it's no excuse for not noting the change in the link, as has been suggested. These are two separate issues. It would have been so simple and honest for him to add the note that the link was his and not in the original quote (or better yet, to leave the link intact and parenthetically provide the link to his own post). In fact, this could still be done. What a shame Mr. Atwood prefers to defend his poor behavior with more poor behavior rather than correct it.
David M on January 21, 2009 10:22 AMJeff, and others, you need to read Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries (Hardcover)
by Krzysztof Cwalina (Author), Brad Abrams (Author)
http://www.amazon.co.uk/Framework-Design-Guidelines-Conventions-Libraries/dp/0321246756
If you understood IDisposable and why it's necessary you wouldn't make stupid posts. Close and Dispose do the same thing, and the framework designers say something to the effect of 'Use a method like Close() if Dispose will confuse the consumer and cause them to wet their pants'
It also goes and warns you to avoid if at all possible, adding IDisposable to your classes, as well as avoiding finalizers if you can. Mostly for the reasons you cite, because junior programmers can't grok it, and because most of the time when people add them, they didn't need to.
The only thing that could be better is for Visual Studio to highlight IDisposable classes so when you see them you think .. "Should I wrap that in a using?".
In summary, you don't need to dispose of anything. Just don't come crying when your app uses ungodly amount of memory or grinds to a halt when it can't connect to the database, or write to that file, and all because you're lazy. But you're allowed to do stupid things because you're the programmer.
The relationship of a program to the computer is like a mans's relationship to his wife. Sorry babe, but I'm not a mind reader.
Thom Kat on January 21, 2009 1:46 PMIn my experience, few people truly understand the inner working of the heap and the GC, and the part IDispose plays. While I agree there's a sea of programmers out there calling Dispose that really don't know why they are doing and what effect is has (if any), I understand why they are doing it. Have you ever build any degree of complex WinForms app and watched the thing leak like a siv? When you look at the code you see no reason why it would leak, but Task Manager show 500+GB and climbing. Your users are screaming at you. Your dates are slipping. Your managers are yelling at you. I bet you this has happened to all those Dipose() callers out there that you're talking about and this is the thing they know to do to not get into that mess again.
Calling dispose will sometimes help you, but guess what - GC has already failed you (or maybe you failed it). Either way, it's very easy to introduce a leak and memory management in .NET is definitely NOT free NOR easy.
Kelly Brownsberger on January 22, 2009 5:20 AMIn my experience, few people truly understand the inner working of the heap and the GC, and the part IDispose plays. While I agree there's a sea of programmers out there calling Dispose that really don't know why they are doing and what effect is has (if any), I understand why they are doing it. Have you ever build any degree of complex WinForms app and watched the thing leak like a siv? When you look at the code you see no reason why it would leak, but Task Manager show 500+GB and climbing. Your users are screaming at you. Your dates are slipping. Your managers are yelling at you. I bet you this has happened to all those Dipose() callers out there that you're talking about and this is the thing they know to do to not get into that mess again.
Calling dispose will sometimes help you, but guess what - GC has already failed you (or maybe you failed it). Either way, it's very easy to introduce a leak and memory management in .NET is definitely NOT free NOR easy.
Kelly Brownsberger on January 22, 2009 5:21 AM> Advocating that programmers not call Dispose (or use "using") and, furthermore, suggesting that they're "obsessive compulsive" for doing so is doing a disservice.
a) developers are, in fact, obsessive-compulsive. Exhibit A: this conversation.
b) the point is to provoke thought about why it's necessary to do this at all. see: http://www.codinghorror.com/blog/archives/000630.html
> Misquoting someone and then ridiculing those who point it out
The *really* funny thing is that I have actually changed quotes in this blog before -- I've modified the words in the quoted text. Quite extensively in fact. Many times. And guess who noticed?
Nobody.
(I'll leave the hows and whys of that as an exercise for the reader. Well, if you're really serious about this "misquoting" business, as promised.)
So pardon me if I don't take your complaint about a hyperlink that used to link to the concept of Worse Is Better, and *still* links to the concept of Worse Is Better, very seriously.
Jeff Atwood on January 22, 2009 6:33 AM>The *really* funny thing is that I have actually changed quotes in this blog before -- I've modified the words in the quoted text. Quite extensively in fact. Many times. And guess who noticed?
I'm not really understanding what your position is here. It almost seems like it is "because my underhanded manipulation of text paraded as quotes went unnoticed before, you have no right to question it now."
Please tell me that isn't your argument.
And people probably didn't notice changes before, however the links were very obvious to most anyone.
Not sure what you think the big agenda is, but it was in pretty poor taste to modify the quote as you did.
Seriously on January 22, 2009 11:11 AMThe 'smackdown model' ... Taking the 'Science' out of Computer Science.
I guess it really indicates exactly the level of intellect you are aiming at with this blog. Apparently you don't think you can have a rational discourse; you need to shove your opinions down peoples throat.
Look, it's fine when you're talking about processors or some gadget, because a lot of that is just opinion, so being opinionated is a good thing. But when we're talking about the science of computer programming, 'smackdown' just doesn't cut it.
Phil on January 22, 2009 1:37 PMI think this article (and the NP-Complete article) confirms Jeff is not as good of a developer as he thinks.
> Personally, I view explicit disposal as more of an optimization than anything else
That is just so wrong.
> because he can do a better job than the garbage collector
Its because the garbage collector only knows about memory. It doesn't know about external resources like database connections.
And now he thinks its fine to misquote people.
Anonymous on January 22, 2009 4:20 PMJust because you don't use C doesn't mean other people don't. C's still the most popular language for new open source projects.
http://www.theregister.co.uk/2009/01/21/open_source_projects_08/
Malloc, pointers, etc., aren't any sort of deep magic. Programmers have written solid code for years using them, and programs using them don't have to stupidly try freeing their memory three times.
Oh, but memory leaks?
In one of my java programs, one one VM (but not on other platforms), it wasn't recognizing some objects in a loop were going out of scope, so it had a tiny memory footprint on all platforms except that one, which memory leaked itself into crashing within a few minutes of running. I had to completely restructure my code so that the stupid GC would free the resources correctly.
This doesn't happen in C.
Bill on January 22, 2009 10:58 PM^one^on
Bill on January 22, 2009 10:58 PMTo the people that said the following does nothing:
sqlConnection = null;
Correct in most cases, however setting to null will inform the GC straight away that the object is not needed, so in theory it could get collected slightly quicker.
The only time I could see the point in doing this would be if you were not using a 'using' block and you had finished early with your object in a very long running method.
rowan on January 23, 2009 1:53 AM@rowan: Setting a variable to null doesn't "inform" the GC of anything. It's not that active.
Furthermore, if sqlConnection is a local variable and doesn't get used anywhere else within the rest of the method, the GC won't consider it a root anyway. For instance:
SqlConnection conn = new SqlConnection(...);
DoSomethingUsingConn(conn);
GC.Collect();
Console.WriteLine("Done");
Here, the "conn" variable doesn't prevent the SqlConnection from being garbage collected at the third line, even though it's still in scope. The GC knows it's not going to be used any more, so doesn't care about its value. This is only true when a debugger isn't attached though.
Jon
Jon Skeet on January 23, 2009 6:14 AMAnd to add to Jon's answer, IIRC artificially setting sqlConnection to null at the bottom of the method may have the effect of keeping it around for longer. (depending on the JIT'er?)
Ger on January 23, 2009 12:40 PM@Ger: I don't *think* it does in .NET. I *think* the JIT is smarter than that... but it's easy enough to test. (I don't have time right now, unfortunately...)
Jon Skeet on January 23, 2009 2:15 PMJeff Atwood wrote:
> The *really* funny thing is that I'm a douche bag. And guess who noticed?
>
> Nobody.
Until now, that is.
Anonymous on January 25, 2009 11:39 AMIt doesn't matter what color you enter.
Wake on January 26, 2009 9:31 AMsqlConnection.Close();
sqlConnection.Dispose();
sqlConnection = null;
...Actually, I'd say that's not weird at all. First you Close the handle, freeing it for use within this session, then you Dispose of it, probably disconnecting it and so freeing it for other sessions, and then you clear the reference because Dispose() doesn't want to clear the entire object. Looks like one of our setups.
Got to love SQL server.
MH on January 28, 2009 11:33 AM@MH: No, that *is* weird, at least in C# (the context of the post, I believe). If you're about to call Dispose() there's no point in calling Close(). Unless it's an instance variable, or you're going to assign it a different value later, there's no need to set it to null.
Hi everyone. You have no control over what the other guy does. You only have control over what you do.
I am from Algeria and also am speaking English, please tell me right I wrote the following sentence: "How to manipulate airline companies ticket pricing computer systems to give you thousands of discounts on your ticket! I had a customer getting a."
Thank you very much :P. Neda.
Neda on April 5, 2009 9:24 PM| Content (c) 2009 Jeff Atwood. Logo image used with permission of the author. (c) 1993 Steven C. McConnell. All Rights Reserved. |