« Object Lust #1: Double feature | Main | Vishy's Indian English Dictionary: French beard »
July 02, 2005
Separation between an application and its host runtime: a case study
The free availability of source code for modern object runtimes such as the .NET CLR and JVM is very helpful in figuring out the internals of these industrial strength runtimes. If you are an independent learner, perusing the source code and somewhat terse specifications of these runtimes goes quite a bit further than visiting your local bookstore for a technical book. Sometimes you end up learning things that you wouldn't otherwise notice when working with these runtimes.
'Hello world' programs in both C# and Java look nearly identical, except for keyword differences. However, whereas Java requires the main method of a class to be both static and public, C# is satisfied with a main method of any accessibility level, so long as it is static. I will quote here two code snippets taken from the source code to the Java and .NET runtimes. In the file ${SRC_ROOT}/java.c from the source jar of the Java Virtual Machine, we see the snippet:
/* Make sure the main method is public */jint mods;
jmethodID mid;
jobject obj = (*env)->ToReflectedMethod(env, mainClass,
mainID, JNI_TRUE);
...
mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, obj),
"getModifiers", "()I");...
mods = (*env)->CallIntMethod(env, obj, mid);
if ((mods & 1) == 0) { /* if (!Modifier.isPublic(mods)) ... */message = "Main method not public.";
messageDest = JNI_TRUE;
goto leave;
}
Compare this to a snippet from ${SRC_ROOT}/clr/src/vm/clsload.cpp:
// Returns true if this is a valid main method?
void ValidateMainMethod(MethodDesc * pFD, CorEntryPointType *pType)
{
_ASSERTE(pType);
// Must be static, but we don't care about accessibility
THROWSCOMPLUSEXCEPTION();
if ((pFD->GetAttrs() & mdStatic) == 0)
ThrowMainMethodException(pFD, IDS_EE_MAIN_METHOD_MUST_BE_STATIC);
These snippets show the main method of an executable class being looked up and validated for passing control into by the object runtime. The comments in the code should make things sufficiently clear. In the JVM implementation, it is mandatory for the main method to be both public and static. In the C# implementation, the comment declares that the runtime doesn't care about the accessibility of the Main method so long as it is static. With the additional restrictions imposed by the JVM implementation, you could, in theory, have multiple methods at the bytecode level named main, all of which take an array of strings as formal parameters, with different degrees of visibility. However, when emitting bytecode/IL, both Java and C# compilers prohibit methods with identical signatures differing solely in accessibility at the language level. In other words, the extra constraint imposed by the JVM implementation isn't because Java offers expanded method-naming features over C#. So, with that argument out of the way, which runtime implementation has the better approach?
In a managed object runtime, it is very easy to coalesce the logic of the runtime and the code it executes into one system, from a point of view external to both. The key question is to what extent internal boundaries are still maintained despite this apparent coalescence. The JVM implementation adopts a purist object-oriented approach. In other words, if the main method is not declared by the class as being visible to the world, it is not visible to the runtime either. The JVM implementation relies on and adheres to well-known definitions of member visibility from object-oriented theory. It does not make any assumptions about the function of a method that just happens to be named main, unless the method fulfils visibility (public) and instantiation (static) criteria. In stark contrast, the CLR implementation drops all pretense and says that if there is a static Main method available at all, it has got to be the entry point for the .NET application. The CLR approach too has its own merits. Naming static methods 'Main' when you don't intend them to be application entry points is extremely poor form for a programmer. Nonetheless, the JVM still makes fewer assumptions about the code it will host. In so doing, it demonstrates a philosophy of maintaining a more rigorous separation between the runtime and the code it is executing. Further evidence of this way of thinking of the runtime and an application as separate entities is seen in how the JVM has to be configured and invoked explicitly with the name of the main class ('$ java [options] MainClass'), rather than simply executing the main class directly ('$ MainClass.exe')
Applications can be written very efficiently if they make many assumptions about the underlying runtime and development environment. For example, exploiting implementation-specific quirks in how compilers generate code leads to application code that is as brittle and unportable as it is efficient. The problem is that such applications have effectively made the compiler and runtime part of themselves. I do not claim that the JVM necessarily maintains a better separation between itself and application code than the CLR. For example, the JVM does not have a counterpart to AppDomains, handy units of code protection and isolation in the CLR. The CLR can set up an application to run in an AppDomain separate from its own, thereby completely isolating itself from unexpected application behavior. However, in the simple matter of the main method of an application, we see a slight divergence in the philosophies of the Java runtime and the CLR. The JVM demonstrates a stricter separation between an application and its host runtime and takes the better approach.
[C# fans: the bad karma I have generated for myself by portraying my primary programming language these days in a negative light will be neutralized by vicious Java-bashing in a future blog entry. Worry not. --V]
Posted by Vishy at July 2, 2005 01:12 PM