Friday, May 25, 2007

Why You Should Ditch C++ For Java

In the 8 years I've been developing in Java, I've heard of numerous arguments about why it is better than C/C++. Some rave about the garbage collection that frees you from (most) memory management tasks. Others are swearing by the thread awareness of the language and the libraries. Or the design patterns that are elegantly embedded in its library implementation. Or the VM abstraction that makes it possible to run the same code in multiple operating systems. And of course you can always find the weirdos that mumble things like type safety and automatic bounds checking. These are all nice indeed, but it is something else that fills my heart with joy each and every time I stumble upon it.

Pretend for a minute that you make a living programming computers. I know, why would someone in his right mind do something like that, but bear with me. Let's also assume that for reasons unknown even to the Creator himself, you are using a development environment (IDE in geek-speak) written in C++. Suppose that while banging merrily at the keyboard you hit an IDE bug. Kaboom!

Segmentation fault. Core dumped.

Or perhaps you got a pretty little window on the screen that informs you that something terrible happened to your IDE and suggests to notify the proper authorities (that is, the IDE maker). After recovering from such a traumatic experience, you will eventually have to relaunch your IDE, curse for the Loss of Unsaved Changes and do them all over again.

Now let's assume you were using an IDE written in Java. Click, click, click: bug. But this time no kaboom. You clicked on some button-shaped thingy but nothing happenned. You click again, nada. The IDE is still there but something went obviously wrong. You call for help and your friendly sysadmin shows up. He digs up the IDE's log file and he shows you what happenned:

java.lang.NullPointerException
at org.eclipse.update.internal.ui.wizards.ReviewPage$TreeContentProvider.getParent(ReviewPage.java:181)
at org.eclipse.update.internal.ui.wizards.ReviewPage.getSite(ReviewPage.java:1370)
at org.eclipse.update.internal.ui.wizards.ReviewPage.access$29(ReviewPage.java:1367)
at org.eclipse.update.internal.ui.wizards.ReviewPage$13.run(ReviewPage.java:816)
at org.eclipse.jface.operation.ModalContext$ModalContextThread.run(ModalContext.java:113)


What do you do? You just forget about clicking that button-shaped thingy ever again (or maybe for a day) and get back to coding frenzy.

The important thing to realize is that in both cases the same problem emerged. A reference or pointer to something was accidentally pointing to something else instead, and it was being forced to behave, perhaps a bit abruptly. After all, we should all be careful where we point to. It's the noble thing to do. But the impact of such an accident was fundamentally different. In the first case the operating system killed the process, while in the second case an exception was propagated up the stack. Both programs are multi-threaded and therefore capable of doing many things in parallel, yet the first one could not avoid having all its threads killed because one of them messed up. Oh, the cruelty! How would that make the other threads feel, I wonder. The Java program on the other hand, rightfully punished the offending thread (to make an example perhaps), while the others were left alone to go about their businesses. My guess is they all chatted quietly afterwards about it and everyone agreed that the grounded thread was a troublemaker, who got what she deserved.

The same thing happens all the time on server programs, too: big, heavy applications that serve legions of users. When the administrators are least expecting it, a stray pointer causes havoc. The service goes down, the call center is flooded with support calls and the administrator's pager goes nuts. That is for C++ server programs, of course. Java-based server applications go on serving people, even while a few threads get kicked in the bottom every now and then. (And in case some smartass mentions using watchdog daemons to restart servers: would you rather have a staff that worked via self-motivation or a staff that needed angry, big dogs to watch them while they do their job?)

So, in a nutshell, that's why you should not be using C++. Not if you pity the poor program's users, that is. Or its undeservedly punished threads. C++ is a hard, cruel language. Come and join me in the heavenly world of Java.

10 comments:

Kostas Georgiou said...

I have to disagree here (whith my sysadmin hat on). Most of the time the tread that died was important, yes your IDE might still have a window open but when that button that doesn't work anymore is Save or Compile what can you do? is the program usefull at this point?

It is even worst on servers, something doesn't work anymore (so the server isn't usefull to anyone any more) but you don't know about it (it hasn't died with a core dump so you can not restart it) and it just sits there doing nothing usefull (or worst damaging data) while all your monitor tools think that everything is OK.

Ignoring the problem (bad pointer, whatever) is not the solution, it is silly to think that "what the hell, I'll continue running my producer thread after the consumer is dead".

past said...

Well, I agree that there are bad bugs and there are worse bugs. And when your luck runs out, it's the "Save" button that gets sick. However, I honestly can't recall many occasions that I encountered such a nasty bug. And I'm not the luckiest kid on the block, mind you.

Instead, what I come across more often than not is a sick "Update" button or perhaps a dizzy "New" button where a Wizard apparently has been bewitched.

And on the server front I have even more conclusive data: it's usually the "GenerateRidiculouslyBigReport" thread that gets smacked, never the "AcceptIncomingConnections" one. Probably, because I haven't tested the former as much as the latter, but you can't quote me on that.

So in my experience, most bugs lurk in corner cases of an application's functionality, because they are likely to be the least tested. And as a consequence of that, the application will likely be in a usable state after a bug appears. You will be able to save your work or fill out some more forms, before someone gets around to fixing the bug.

The point I was really trying to make was to promote resilience, not recklessness. We're very much in agreement on that one.

chstath said...

Would like me to remind you how often Eclipse used to lock up your Windows-based workstation leaving you with no option but to cold reboot ? You hadn't press any button and nothing was in the log (at least most of the times). Do you remember how many times Eclipse just disappeared before my eyes while I was just typing again with nothing in the log ?
C++ also has exceptions and exception handling and killing the app because of a NullPointerException somewhere is something that can very well happen in Java, too. If you leave untested corner cases in a java app it might behave like a C++ one. But this is the case in C++ too. It 's the programmer not the language.

past said...

You are conflating different issues here. The incidents you mention were caused by out of memory errors, usually in the permanent generation space. This is quite different than an NPE in Java code and it's arguably the VM's (GC's) bug, not your (Java) code's. This particular behavior of the Sun JVM has been a long-time joke among other VM developers, like IBM's or BEA's whose VMs auto-tune the permanent generation space along with the others.

The thrashing issue with Windows XP Home that (pretty much) required a reboot, is another kind of bug (or feature if you ask MS people) in the memory management code of the OS that behaves quite badly under low memory conditions.

C++ does indeed have exceptions, but cannot throw one when dereferencing a null pointer. Instead the OS blocks the action and sends a signal (usually SIGSEGV, SIGBUS or SIGABRT) to warn the process. The process's death is an unfortunate side effect of the fact that signals get delivered to processes not threads (which is largely caused by the fact that the language is thread ignorant). Java programs by definition never get killed in such a case, that's when an NPE is thrown.

I agree that the developer always matters, but a huge benefit of modern languages like Java is that they make whole classes of errors impossible to happen, like the one I described in the post.

adamo said...

Real programmer cat > a.out directly :)

Seriously though, this is not a reason to dump C++. Hell, if you really need an IDE go out and learn Emacs!

So this post is really about ditching IDEs written in C++

past said...

Don't read too much into the specific application I used as an example. It's just that: an example. The same behavior can be observed when comparing, say BitTorrent clients, like Azureus (Java) and eDonkey2000 (C++).

In fact, what I was trying to convey is the fundamental difference in the reliability guarantees that a large multithreaded application can provide when it is written in Java versus a similar application written in C++.

Of course it's not just Java that possesses these characteristics, C# for example has them, too. Not sure about Lisp though, but from what I had read once, emacs-lisp does not have threads (yet?), so it wouldn't compare favorably either :-)

Dionysios G. Synodinos said...

"The next C++ standard will provide direct support for threads, including a model of memory, ... all » atomics, variables, launching, scheduling, synchronization, and termination."

Of course we still have to see how well all this will be implemented but maybe this is of an interest to you: http://video.google.com/videoplay?docid=3528799355371049884

past said...

There was an article in the last issue of the USENIX magazine, by Boehm, Lea and Pike if I recall correctly, that talked about it. It could address this problem if it mandated some way to deliver signals to threads, but I'm not sure it was covered.

adamo said...

past, I do not use any of the software that you mention, therefore I am not bitten by it. Plus, I really hate threads :)

In fact, what I was trying to convey is the fundamental difference in the reliability guarantees that a large multithreaded application can provide when it is written in Java versus a similar application written in C++.

So you want it guaranteed by the language and not by the programmer?

past said...

Plus, I really hate threads :)

That's because you have encountered them in a C/C++ context, probably :-)

So you want it guaranteed by the language and not by the programmer?

Yes, for making sure the programmer can't mess them up (too much) and has a chance to understand them without a PhD :-)

Creative Commons License Unless otherwise expressly stated, all original material in this weblog is licensed under a Creative Commons Attribution 3.0 License.