JDK-4498653 : reflective access of static field doesn't trigger class initialization
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang:reflect
  • Affected Version: 1.4.0
  • Priority: P1
  • Status: Closed
  • Resolution: Fixed
  • OS: generic,windows_2000
  • CPU: generic,x86
  • Submitted: 2001-08-31
  • Updated: 2012-09-28
  • Resolved: 2001-09-12
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
1.4.0 beta3Fixed
Related Reports
Duplicate :  
Duplicate :  
Relates :  
Description
jdk 1.4 beta 3, build 78. Netbeans IDE doesn't start correctly anymore with this jdk build, due to this bug. Previous builds (77 and further) worked well, so this seems to be some kind of regression. 

Here is thrown exception, together with debug informations that were put into ObjectStreamClass.matchFields method:  

Comparing isStartup
Local desc idHashCode: 5981330
Comparing tabbedContainerUI
Local desc idHashCode: 5981330
Comparing uiMode
Local desc idHashCode: 5981330
Comparing current
Local desc idHashCode: 5981330
Comparing mainWindowBounds
Local desc idHashCode: 5981330
Comparing oldScreenSize
Local desc idHashCode: 5981330
Comparing workspaces
Local desc idHashCode: 5981330
Comparing mainWindowBounds
Local desc idHashCode: 5981330
Comparing tabbedContainerUI
Local desc idHashCode: 5981330
local field: I tabbedContainerUI
field: Ljava/lang/Integer; tabbedContainerUI
Fri Aug 31 14:26:27 CEST 2001: java.io.InvalidClassException: org.netbeans.core.windows.WindowManagerImpl$SerializationReplacer; incompatible types for field tabbedContainerUI
Annotation: Type= 5, name = Error deserializing window system.
java.io.InvalidClassException: org.netbeans.core.windows.WindowManagerImpl$SerializationReplacer; incompatible types for field tabbedContainerUI
        at java.io.ObjectStreamClass.matchFields(ObjectStreamClass.java:1970)
        at java.io.ObjectStreamClass.getReflector(ObjectStreamClass.java:1865)
        at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:499)
        at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1499)
        at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1413)
        at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1604)
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1252)
        at java.io.ObjectInputStream.readObject(ObjectInputStream.java:323)
        at org.openide.util.io.NbMarshalledObject.get(NbMarshalledObject.java:90)
[catch] at org.netbeans.modules.projects.PSupport.updateWindowManager(PSupport.java:811)
        at org.netbeans.modules.projects.PSupport.projectOpen(PSupport.java:310)
        at org.netbeans.core.NbProjectOperation.setProject(NbProjectOperation.java:129)
        at org.netbeans.core.NbProjectOperation.setOpeningProject(NbProjectOperation.java:185)
        at org.netbeans.core.NbProjectOperation.openOrCreateProject(NbProjectOperation.java:172)
        at org.netbeans.core.NonGui.run(NonGui.java:520)
        at org.netbeans.core.Main.run(Main.java:216)
        at org.openide.TopManager.initializeTopManager(TopManager.java:120)
        at org.openide.TopManager.getDefault(TopManager.java:81)
        at org.netbeans.core.Main.main(Main.java:325)
        at org.netbeans.core.TopThreadGroup.run(TopThreadGroup.java:90)

Exception is saying that class stream descriptor of serialized data and class stream desciptor defined in class WindowManagerImpl.SerializationReplacer are incompatible. However, this shouldn't be true, because data was serialized with exactly the same description of persistent fields.

Reproduction steps:
1) Start the IDE first time, no serialized info exists, all is ok
2) Exit the iDE, data are written to the disk
3) Restart IDE, and during data read you'll get the exception mentioned above.

From looking at the debug prints, I found out that the field "tabbedContainerUI" must change its type somehow, which is strange. I define this field as type Integer.class in WindowManagerImpl.SerializationReplacer. However, debug prints show that it was read from the disk as Integer.TYPE, not Integer.class.
Even more, it looks like field "tabbedContainerUI" was matched twice, first match was succesfull while second failed.

Bad thing is that I'm not able to reproduce described wrong behaviour on some small test. I really don't know why, but only reliable reproduction case is Netbeans IDE itself.

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: merlin-beta3 FIXED IN: merlin-beta3 INTEGRATED IN: merlin-beta3 VERIFIED IN: merlin-beta3
14-06-2004

SUGGESTED FIX Ensure that reflective static field operations trigger class initialization.
11-06-2004

EVALUATION This bug is caused by behavior changes in reflection with respect to obtaining static field values. In 1.4-B77 (and earlier builds), calling get() on the reflective Field object for a static field would trigger initialization of the class declaring the field, if the class wasn't already initialized. 1.4-B78 was the first build including the fix for 4490864 ("(Startup) First-time reflective static field access should be fast"), in which static field reflective operations were reimplemented to use a new entry point in sun.misc.Unsafe. Apparently, retrieving static field values via sun.misc.Unsafe doesn't trigger class initialization, whereas the previous bytecode-based implementation did. Here's a simple test program which demonstrates the difference: import java.lang.reflect.*; class Bar { static { System.out.println("Bar.<clinit> called"); } static Object obj = new Object(); } public class Foo { public static void main(String[] args) throws Exception { Class cl = Class.forName("Bar", false, Foo.class.getClassLoader()); if (cl.getDeclaredField("obj").get(null) == null) { throw new Error(); } } } Run with B77 (as well as 1.3.1, 1.3 and 1.2.2 FCS), the program exits normally; with B78, Field.get() returns null (since Bar's static initializer has not run yet) and the test fails. The reason this causes the NetBeans startup problem is that the serializable class in question uses the static serialPersistentFields field to declare its serializable fields (at least one of which happens to differ in type from the actual field that backs it). The first time that NetBeans is started up (with no user configuration/desktop saved), an instance of the serializable class is created "normally" (i.e., outside of serialization), so the static initializer of the class has executed by the time an instance of it is written to file. As a result, the serialized representation of the class in the written stream contains the intended set of fields (specified with serialPersistentFields). The second time that NetBeans starts up, it tries to load the desktop from the saved serialization stream. Here, the serializable class is not used by NetBeans prior to deserialization, so the static initializer for the class has not yet run. When serialization looks up the value of serialPersistentFields for the class, it receives null since the field is uninitialized; this causes serialization to fall back to the "default" serializable fields for the class, which happen to conflict type-wise with the serialPersistentFields-declared fields. ###@###.### 2001-08-31 Fix will be to provide new VM entry point to ensure that a class is initialized. Should show up in next week's build (B79). ###@###.### 2001-09-06 Fixed in B79. ###@###.### 2001-09-12
06-09-2001