# Monday, 17 November 2003
« New Snapshot | Main | More On Finalization »
C# Destructor Considered Harmful

After last week's Java finalization bashing, it turns out that C# is even more broken.

A C# Destructor Cannot Be Sealed

This is really bad! If you recall my example of a proper class that uses finalization correctly, you might remember that the class was final. I still  highly recommend this, but in some scenarios it might be preferable to allow others to extend your class. In such cases it is highly recommended that you make your finalize method final. Otherwise the subclasser might override finalize and forget to call your finalize method.

If you wrap an unmanaged resource and your class is non-final and it is exposed to untrusted code, you must make your finalize method final.

If you don't, the untrusted code can (intentionally or not) create a subclass of your class, override finalize (not call super.finalize()) and start leaking unmanaged resources that will never be cleaned up as long as the JVM is running.

Back To C#

To see why the C# design is a problem, we only need to look at System.WeakReference. It is a public non-sealed class that wraps an unmanaged resource (a GCHandle), the destructor is obviously not sealed and it does not require any privileges to use, this equals a recipe for disaster. Untrusted code can leak GCHandles that will never be reclaimed as long as the CLR is running [1]. Not even when the AppDomain is unloaded!

IMO, the destructor syntax should be deprecated and Finalize should be treated like any other method. This current design is hardly a pit of success.

[1] While the C# destructor is nice enough to always call the base class Finalize method, other languages (e.g. ILASM or VB.NET) don't require this.

Monday, 17 November 2003 14:19:49 (W. Europe Standard Time, UTC+01:00)  #    Comments [10] Tracked by:
"best diet ever pill" (best diet ever pill) [Trackback]
"online free roll poker tournaments" (online free roll poker tournaments) [Trackback]

Monday, 17 November 2003 15:40:40 (W. Europe Standard Time, UTC+01:00)
Huh...csc inserts an base.Finalize() in the destructor of any class. So..in your example...
the System.WeakReference dctor will ALWAYS be called ! Then what is the problem ?!
Monday, 17 November 2003 15:55:02 (W. Europe Standard Time, UTC+01:00)
See [1]. Not all code is always written in C#.
Tuesday, 18 November 2003 18:32:13 (W. Europe Standard Time, UTC+01:00)
I'm working towards trying to deploy an ikvm-compiled version of nrdo in my work environment. 99% of it is working - I can run it fine when I disable interaction with sourcesafe - but I'm getting a nullpointer exception when sourcesafe interaction is enabled.

Obviously I'm not expecting you to debug nrdo for me ;) But I'm having a hard time tracking down exactly whereabouts the problem is occurring, because when run with Java it doesn't happen, but when run with IKVM I don't have source file line numbers on my stacktraces.

Is there any possible way of getting enough debugging information to be included (optionally) into ikvmc'd assemblies that line number info could make it into the stack traces? If not, I'll just add lots of System.out.println()s and see if I can track down which call is nullpointering, but it would certainly save me a lot of time if it were possible.
Stuart
Tuesday, 18 November 2003 18:40:48 (W. Europe Standard Time, UTC+01:00)
One other tiny feature request: Any chance of not printing the full stack trace from ClassNotFound errors that are being successfully caught and handled by ikvmc? I've put the compile of Microsoft's SQL server JDBC driver into my nrdo compile batch file for the benefit of anyone else trying to compile a working IKVM'd nrdo setup, but now I get spammed with pages and pages worth of ClassNotFound stacktraces (for com.merant and sun.io stuff) every time I compile.
Stuart
Tuesday, 18 November 2003 22:26:45 (W. Europe Standard Time, UTC+01:00)
Actually it looks like there's a real bug in the stack traces emitted by IKVMC'd java code. The stack trace I'm seeing shows one method A() being called by another method B(), but A() never actually calls B() (although it probably calls another method C() that calls B()). Possibly the Java compiler is inlining C(), which is probably private and only called in one place, but I've never seen stacktraces that skip methods in java code so presumably Java can handle that somehow.

This also leads me to question whether the nullpointer exception I'm getting is even in the method it claims to be in, because *that* method also is the only place to call some private methods. So it's even harder than I previously thought to figure out where the exception is actually coming from.
Stuart
Tuesday, 18 November 2003 22:45:18 (W. Europe Standard Time, UTC+01:00)
I think I've tracked down my nullpointer, although my comments about stacktraces are still valid. The actual bug may be a classpath one, because I'm still blurry on where the distinction lies between Classpath and IKVM-specific code (it would be easy if Classpath would get around to putting all native methods in VM* classes ;) ).

The problem seems to be that new java.io.File("\\\\server\\share\\path").getCanonicalFile().toString() returns '\server\share\path' (only one backslash at the beginning). This then gets interpreted as a path relative to the root of the current drive and starts looking for a directory that doesn't exist, which ultimately winds up being a nullpointer later on in my code.

This is probably a much easier fix than getting line numbers into stacktraces, right? ;)
Stuart
Wednesday, 19 November 2003 10:02:54 (W. Europe Standard Time, UTC+01:00)
>>Is there any possible way of getting enough debugging information to be included (optionally) into ikvmc'd assemblies that line number info could make it into the stack traces?<<

There is the -debug option for ikvmc, but it doesn't work very well. It also suffers from the fact that .NET doesn't seem to generate line number tables for native code if the code is not running in the debugger.

>>One other tiny feature request: Any chance of not printing the full stack trace from ClassNotFound errors that are being successfully caught and handled by ikvmc? <<

That's an easy one. Just remove " || isStaticCompiler" from line 87 of vm.cs. I'll remove it from source as well.

>>Actually it looks like there's a real bug in the stack traces emitted by IKVMC'd java code.<<

The .NET JIT (sometimes) inlines methods, inevitably this causes the method not to show up in stack traces. Run your code from a debugger to prevent this from happening.

>>I think I've tracked down my nullpointer<<

Cool.

>>The problem seems to be that new java.io.File("\\\\server\\share\\path").getCanonicalFile().toString() returns '\server\share\path' (only one backslash at the beginning). <<

I think that Classpath still has quite a few bugs in file path handling on Windows. Most Classpath developer run on a *nix flavor. A while ago I looked into it, but at the time I concluded it would be a big job to fix it.
Wednesday, 19 November 2003 15:25:17 (W. Europe Standard Time, UTC+01:00)
I'll try out the -debug option and launching my programs from a debugger.

In the interim I've worked around the CanonicalFile bug by writing and using my own static canonicalFile(File) method which treats names starting in "\\" as already canonical, and otherwise defers to the default. This isn't perfect because a filename starting in "\\" might actually not be canonical, because it might still contain "..\" somewhere. But it's good enough for now to get my code to work.
Stuart
Wednesday, 19 November 2003 15:36:18 (W. Europe Standard Time, UTC+01:00)
Found another bug: If you use the Runtime.exec() variant that takes a String[] env argument, and if that array contains a variable name that is already defined in the global environment, you get an ArgumentException saying "item has already been added". This one seems to be caused by line 118 in DotNetProcess.java which does the Java equivalent of the C# code si.EnvironmentVariables.Add(key, val).

If I were writing in C# I'd solve this by writing something like
si.EnvironmentVariables[key] = val; instead. Is there any way to access an indexer like that from Java code?
Stuart
Wednesday, 19 November 2003 16:37:18 (W. Europe Standard Time, UTC+01:00)
>>Found another bug: If you use the Runtime.exec() variant that takes a String[] env argument, and if that array contains a variable name that is already defined in the global environment, you get an ArgumentException saying "item has already been added". This one seems to be caused by line 118 in DotNetProcess.java which does the Java equivalent of the C# code si.EnvironmentVariables.Add(key, val).<<

Oops. Stupid mistake.

>>If I were writing in C# I'd solve this by writing something like
si.EnvironmentVariables[key] = val; instead. Is there any way to access an indexer like that from Java code?<<

Usually, indexers are exposed as set_Item/get_Item methods (technically a language can use whatever method names it wants, the DefaultMember attribute on a type determines which property acts as the indexer).
Name
E-mail
Home page

I apologize for the lameness of this, but the comment spam was driving me nuts. In order to be able to post a comment, you need to answer a simple question. Hopefully this question is easy enough not to annoy serious commenters, but hard enough to keep the spammers away.

Anti-Spam Question: What method on java.lang.System returns an object's original hashcode (i.e. the one that would be returned by java.lang.Object.hashCode() if it wasn't overridden)? (case is significant)

Answer:  
Comment (HTML not allowed)  

Live Comment Preview