Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsWhite Papers
Discussion GroupsFirst AidDatabasesJavaBeansGUIJava 3DVirtual MachineCORBASecurityToolsGeneral
Java DirectoryOpen Source ProjectsSample Book ChaptersUser GroupsWeb Resources
Related Topics
Databases.NETMore Topics ...

Java Forum / General / February 2006

Tip: Looking for answers? Try searching our database.

Java 5 classloader not making static calls.

Thread view: 
Kenny - 13 Feb 2006 18:12 GMT
Hello,

I'm having the problem that classes in java 1.5 do not seem to run
their static elements until the class is first used.

For example in java 1.4 simply making the Class object would cause the
static's to run:
class = a.b.c.TestClass.class

In java 1.5 this no longer happens until you do something like:
variable = a.b.c.TestClass.staticField;

The TestClass contains things like:
   static public Object staticField = blah();

I've been searching for information on why this is, or how to have the
class initalize itself without having to know what type of class it is.
Can anyone offer any suggestions?

Thanks in advance,

Kenny
Chris Uppal - 13 Feb 2006 20:00 GMT
> I'm having the problem that classes in java 1.5 do not seem to run
> their static elements until the class is first used.
> For example in java 1.4 simply making the Class object would cause the
> static's to run:
> class = a.b.c.TestClass.class

Hmmm, that's nasty.

I believe that it's because class literals are now compiled into a single ldw
bytecode (with newly extended semantics) rather than into (indirectly) a call
to Class.forName().  If you compile code that uses a class literal with a 1.4
JDK, or use the -target 1.4 flag with the 1.5 JDK, then the old-style bytecodes
are emitted, and initialisation takes place as before.

The JLS3 is completely unambiguous that this new behaviour is correct (and
therefore that JDK 1.4 was buggy in this respect), but I suspect that's because
the language of Section 12.4.1 has simply not been updated for class literals
(and therefore was incorrect when applied to JDK 1.4 too).  That's to say,
think it's a bug (unless someone can show that nothing "bad" can possibly
happen because a class has not been initialised as a result of referring to its
class literal).

As to how to fix it, calling simple methods of the Class object don't have any
effect, so the only workaround I can think of is to execute something like:

   Class c = my.stuff.Example;
   Class.forName(c.getName(), true, c.getClassLoader());

which is not quite ideal...

Can you describe what problems this new behaviour causes for you ?

   -- chris
Kenny - 13 Feb 2006 20:24 GMT
Thank you very much for your reply. That worked perfectly (although I
agree, not idea at all).

I can better explain the problem by sending a little code, but I must
prefix it with the fact that I did not write this.

The code is basically an Enum, and a global enum repository. It looks
basically like (I'll cut out what I can):

class AEnum
{
   private static AEnum One = (AEnum) EnumRegistry.add(new
AEnum("One"));
   private static AEnum Two = (AEnum) EnumRegistry.add(new
AEnum("Two"));
   // private constructor
}

class EnumRegistry
{
   private static Map classMap = new HashMap();
  public static synchronized Object add(Object enumerationObj)
   {
       java.util.HashMap enumMap;  // A mapping for a particular
class, value->enum

       if (classMap.containsKey(enumerationObj.getClass()))
       {
           enumMap = (HashMap)
classMap.get(enumerationObj.getClass());
       }
       else
       {
           enumMap = new HashMap();
       }
       enumMap.put(enumerationObj.toString(), enumerationObj);
       classMap.put(enumerationObj.getClass(), enumMap);
       return enumerationObj;
   }

   public static synchronized Object
   getEnum(Class enumClass, String value) throws EnumException
   {
       HashMap enumMap = (HashMap) classMap.get(enumClass);
       if (enumMap == null || !enumMap.containsKey(value))
       {
           throw new EnumException("enum value: '" + value + "' not
found");
       }
       return enumMap.get(value);
   }
}

The code that ends up calling this basically goes:
AEnum blah = (AEnum) EnumRegistry.getEnum(AEnum.class, "One");

Now this use to cause AEnum.class's static fields to be created, so it
would populate the registry, but it doesn't.

Thanks again for your quick reply,

Kenny
Stefan Schulz - 14 Feb 2006 07:59 GMT
I would strongly suggest not to use such black magic anyway, but yes -
this code will no longer trigger a initialization of AEnum (why should
it?) and therefore does not populate your map.

However, seriously - could you try and be a little less obscure? Who is
supposed to "get" this behaviour if more then these two classes are
involved? I consider myself not the cleanest coder around, but this is
beyond my pain threshold... and as you could see, error-prone too.
Statics should be handled extra carefully, since much  obscure
behaviour can happen (finals visibly changing value, class init or no
class init...)
Chris Uppal - 14 Feb 2006 11:49 GMT
> The code is basically an Enum, and a global enum repository. It looks
> basically like (I'll cut out what I can):
[quoted text clipped - 6 lines]
> AEnum("Two"));
>     // private constructor

So you (or the code's original author) are effectively doing reflection "by
hand".  It might have been better to use reflection explicitly in whatever part
of the overall design actually calls for it (you didn't show that bit, and
anyway it's not relevant here).

BTW -- just as an aside -- I don't know why Stefan finds this so objectionable.
It seems perfectly clear to me.  It may not be the best approach to the
underlying design problem (or there again it may be the best, who knows ?), but
/given/ this approach, I don't see much wrong with the implementation.

   -- chris
John C. Bollinger - 15 Feb 2006 03:22 GMT
> BTW -- just as an aside -- I don't know why Stefan finds this so objectionable.
> It seems perfectly clear to me.  It may not be the best approach to the
> underlying design problem (or there again it may be the best, who knows ?), but
> /given/ this approach, I don't see much wrong with the implementation.

I'm with Stefan inasmuch as before JLS3 it was always somewhat vague
exactly when a class was to be initialized, therefore Kenny's approach
could not be assumed reliable in Java 1.4 or earlier.  It depends on an
implementation detail of Sun's (I presume) Java implementation; as such,
it should not be shocking that a software update broke it.

Signature

John Bollinger
jobollin@indiana.edu

Chris Uppal - 15 Feb 2006 09:23 GMT
> I'm with Stefan inasmuch as before JLS3 it was always somewhat vague
> exactly when a class was to be initialized,

"Vague"?  The only vagueness in the JLS2 is the same as that I mentioned in the
JLS3 (in fact the wording appears to be more or less the same -- I didn't
notice any changes anyway).  Under the wording of the JLS2 it always has been
flat-out illegal for a class literal expression to cause class initialisation.
Under the JL3 wording it is similarly illegal.

> therefore Kenny's approach
> could not be assumed reliable in Java 1.4 or earlier.  It depends on an
> implementation detail of Sun's (I presume) Java implementation; as such,
> it should not be shocking that a software update broke it.

If we take the JLS at face value, then Kenny's (predecessor's) code is not
depending on an implementation detail, it is depending on a buggy
implementation -- the difference is subtle but real ;-)

But I continue to think that the behaviour required by the JLS is wrong
(violates principle of least astonishement for one thing), and that JDK 1.4.x
was correct, and JDK 1.5 is in error.

   -- chris
Kenny - 15 Feb 2006 15:37 GMT
Yes. I plan to move this code to 5.0 syntax as soon as I can, but first
all the unit tests have to pass with as little change as possible.

For a little more background on this. I didn't write it, but I know who
did, and I know the constraints they were under at the time (no
reflection, no singletons, etc).

Kenny
Roedy Green - 13 Feb 2006 22:12 GMT
On Mon, 13 Feb 2006 20:00:03 -0000, "Chris Uppal"
<chris.uppal@metagnostic.REMOVE-THIS.org> wrote, quoted or indirectly
quoted someone who said :

>> I'm having the problem that classes in java 1.5 do not seem to run
>> their static elements until the class is first used.

this sounds like "if a tree falls in the forest, when there is no one
to hear it, does it make a sound" sort of questions.

In what sort of instance does it make a difference precisely when the
statics are initialised? Is this a matter of where you catch
exceptions in static init code?
Signature

Canadian Mind Products, Roedy Green.
http://mindprod.com Java custom programming, consulting and coaching.

Chris Smith - 15 Feb 2006 15:50 GMT
> On Mon, 13 Feb 2006 20:00:03 -0000, "Chris Uppal"
> <chris.uppal@metagnostic.REMOVE-THIS.org> wrote, quoted or indirectly
[quoted text clipped - 9 lines]
> statics are initialised? Is this a matter of where you catch
> exceptions in static init code?

It makes a difference when the static initializer for a class has a
side-effect outside of the scope of the class.  For example, it was
originally considered acceptable to load a JDBC driver by using a Class
literal... but it's now known that you have to use the longer overload
of Class.forName or call newInstance() to create an object of the driver
in order to ensure that the class is loaded.

Signature

www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation

Adam Maass - 14 Feb 2006 03:07 GMT
> Hello,
>
[quoted text clipped - 14 lines]
> class initalize itself without having to know what type of class it is.
> Can anyone offer any suggestions?

The difference in behavior you've noted is allowed by the language and
platform specifications, and has been allowed for several generations of
JVMs. It just so happens that the 1.4 Sun VMs did the "class initialization"
on load, rather than waiting for "first use."

-- Adam Maass
Chris Uppal - 14 Feb 2006 11:38 GMT
> The difference in behavior you've noted is allowed by the language and
> platform specifications, and has been allowed for several generations of
> JVMs. It just so happens that the 1.4 Sun VMs did the "class
> initialization" on load, rather than waiting for "first use."

I don't think that's true.  Assuming that the wording in the JLS is deliberate,
the 1.4 series JVMs were all seriously buggy in this respect.  The JLS does not
leave it /open/ whether initialisation happens in this case -- it clearly[*]
states that initialisation is /not/ permitted.  No "difference in behavior" is
"allowed by the language and platform specifications".

As I have said, I believe that the JLS itself is at fault here, and thus that
the 1.5 series JVMs are /also/ at fault, wheras the 1.4 series were correct.
I'm willing to be persuaded otherwise, but that's my current opinion.  Whatever
the truth of the matter, the /change/ in behaviour is not a good thing.

(
[*] There's some fudged wording in the "spec":

   Invocation of certain reflective methods in class Class
   and in package java.lang.reflect also causes class or
   interface initialization.

"certain reflective methods" -- great!  That /really/ ties down the
specification...  Anyway, neither of the cases mentioned apply here, so we can
rely on the next sentence:

   A class or interface will not be initialized under any
   other circumstance.
)

   -- chris
Adam Maass - 15 Feb 2006 07:04 GMT
>> The difference in behavior you've noted is allowed by the language and
>> platform specifications, and has been allowed for several generations of
[quoted text clipped - 10 lines]
> behavior" is
> "allowed by the language and platform specifications".

I believe the only guarantee the specs made was that a class is initialized
before first use -- they make no guarantee of when that initialization
actually happens; some VMs do it on class load, others do it just before
first use. (In this respect, it's similar to the loose guarantee about the
timing of garbage collection.) I'm too loopy at the moment to read the
specifications so I can quote, but I will get to it soon.

-- Adam Maass
Chris Uppal - 15 Feb 2006 09:29 GMT
> I believe the only guarantee the specs made was that a class is
> initialized before first use -- they make no guarantee of when that
[quoted text clipped - 3 lines]
> moment to read the specifications so I can quote, but I will get to it
> soon.

When the loopyness wears off, you'll find that initialisation is specified to
happen on first use (for a specified meaning of "use") and that it is
specifically /not/ allowed to happen earlier.  Many (all?) of the prior phases
of class loading /are/ allowed to happen eagerly, it is only the execution of
<cinit> that has a fixed temporal semantics.

It's in section 12.4.1.

   -- chris
Roedy Green - 15 Feb 2006 12:05 GMT
On Wed, 15 Feb 2006 09:29:27 -0000, "Chris Uppal"
<chris.uppal@metagnostic.REMOVE-THIS.org> wrote, quoted or indirectly
quoted someone who said :

>When the loopyness wears off, you'll find that initialisation is specified to
>happen on first use (for a specified meaning of "use") and that it is
>specifically /not/ allowed to happen earlier.  Many (all?) of the prior phases
>of class loading /are/ allowed to happen eagerly, it is only the execution of
><cinit> that has a fixed temporal semantics.

you want to allow the compiler writer as much latitude as possible to
optimise, e.g. preemptively load classes it remember you used last
time -- but it can't run their initalisers until you actually DO use
them.
Signature

Canadian Mind Products, Roedy Green.
http://mindprod.com Java custom programming, consulting and coaching.

Adam Maass - 17 Feb 2006 05:03 GMT
>> I believe the only guarantee the specs made was that a class is
>> initialized before first use -- they make no guarantee of when that
[quoted text clipped - 14 lines]
>
> It's in section 12.4.1.

Of both the second and third editions of the spec. There is the noted fudge
factor about "some reflective methods will cause initialization."
(Paraphrase.) Evaluation of the the class literal apparently was in that set
in 1.4.x but is no longer in that set as of 1.5.

Bleh. I wish the spec weren't so vague on this point.

-- Adam Maass
Kenny - 15 Feb 2006 15:46 GMT
It seems the change was at least documented. I came across:
http://java.sun.com/j2se/1.5.0/compatibility.html

Under:
Incompatibilities in the Java 2 Platform Standard Edition 5.0 (since
1.4.2)
#5 "Previously, evaluating a class literal (for example, Foo.class)
caused the class to be initialized; as of 5.0, it does not. Code that
depends on the previous behavior should be rewritten."

I agree as well, the change doesn't seem like a good thing. Hopefully
it was done for a good reason. The forName() works great for the
moment, and I'll rewrite that section after all the tests pass.

Kenny
Chris Uppal - 16 Feb 2006 10:37 GMT
> It seems the change was at least documented. I came across:
> http://java.sun.com/j2se/1.5.0/compatibility.html

Aha.   Then at least it's deliberate -- which is something...

Thanks for the pointer.

   -- chris
Adam Maass - 18 Feb 2006 05:59 GMT
> Hello,
>
[quoted text clipped - 18 lines]
>
> Kenny

For what it's worth, I consider non-trivial class initialization a code
smell. There are too many things that can go wrong with it. Among them:

* What happens if class initialization throws exceptions?
* Class initialization often happens far earlier than I usually, naively,
expect it to.
* And, as we've seen, the actual behavior of when class initialization
happens isn't quite predictable.

-- Adam Maass
Stefan Schulz - 19 Feb 2006 02:10 GMT
> For what it's worth, I consider non-trivial class initialization a code
> smell. There are too many things that can go wrong with it. Among them:
>
> * What happens if class initialization throws exceptions?

An ExceptionInInitializerError gets thrown

> * Class initialization often happens far earlier than I usually, naively,
> expect it to.

Which is why i would try and keep its effects strictly contained within
that one class. How, and when the class performs its internal
housekeeping is its own business, but i would strongly suggest not
mucking around too much.

> * And, as we've seen, the actual behavior of when class initialization
> happens isn't quite predictable.

It probably is, however it will depend on the bytecode instead of the
source code, and therefore is highly arcane, and not something you
should rely on.
Adam Maass - 19 Feb 2006 20:34 GMT
>> For what it's worth, I consider non-trivial class initialization a code
>> smell. There are too many things that can go wrong with it. Among them:
>>
>> * What happens if class initialization throws exceptions?
>
> An ExceptionInInitializerError gets thrown

That's the immediate effect, yes, but I've seen that Error logged to a log
file and the rest of system just go blithely on until some other Throwable
gets raised -- typically a NoClassDefFoundError or some such. Those are
blazingly hard to debug.

-- Adam Maass
Stefan Schulz - 19 Feb 2006 21:57 GMT
> >> For what it's worth, I consider non-trivial class initialization a code
> >> smell. There are too many things that can go wrong with it. Among them:
[quoted text clipped - 7 lines]
> gets raised -- typically a NoClassDefFoundError or some such. Those are
> blazingly hard to debug.

Those should not happen. Anyone just logging and discarding an Error
should be drawn and quartered. An Error means something went so majorly
wrong that the system can't be expected to recover. Okay, so sometimes
this might not be true, and sometimes, it might be possibly to at least
fail gracefully. But just discarding one is about the worst thing you
can do.
Adam Maass - 20 Feb 2006 06:52 GMT
>> >> For what it's worth, I consider non-trivial class initialization a
>> >> code
[quoted text clipped - 18 lines]
> fail gracefully. But just discarding one is about the worst thing you
> can do.

This is Tomcat, at least in some of its 4.1.x incarnations.

The problem is partly due to the notion of multiple webapps residing in the
same servlet container -- if one of them raises ExceptionInInitializerError,
that one webapp is bad, but does not imply that the whole JVM instance is
bad. Therefore, the JVM itself does not shut down. Webapps (can) have
multiple initialization hooks, so if one part of it raises
ExceptionInInitializerError, in principle, the other parts might still
successfully initialize and run. Of course, that generally isn't the case...

I wonder if the Tomcat 5 series have addressed this any better... I'll have
to go check....

-- Adam Maass


Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2009 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.