Parsing the initialization process of Java classes and objects

Source: Internet
Author: User
Tags jboss server
Parse the initialization process of Java classes and objects-general Linux technology-Linux programming and kernel information. The following is a detailed description. Class initialization and object initialization are two very important links in the type lifecycle of JVM management. Google has published many articles on the class loader System, however, there are not many articles on class initialization and object initialization, especially the articles on analyzing bytecode and JVM layers.

This article mainly analyzes the entire process of class and object initialization, introduces a practical problem, converts the source code to the JVM bytecode, and comprehensively parses the key points in the JVM execution process, some internal theoretical knowledge about JVM specifications and JVM is inserted in this article, and the collaboration between object initialization and class initialization and possible conflicts are introduced in the form of a combination of theory and practice.

Problem Introduction

Recently, I am debugging an enumeration parser program that loads more than 10 thousand enumeration codes in the database into the cache, to quickly locate the enumeration Code and all enumeration elements of a specific enumeration class, this class uses two policies to create a memory index while loading the enumeration code. This class is a public service class that is used at all levels of the program, so I implement it as a singleton class. This class runs normally before I adjust the class instantiation statement position, but when I adjust the class instantiation statement to the static initialization statement, my program no longer works for me. The following is the simplified sample code:

[LIST 1]

Package com. ccb. framework. enums;
Import java. util. Collections;
Import java. util. HashMap;
Import java. util. Map;
Public class CachingEnumResolver {
// All problems with a single-State instance are caused by this row
Private static final CachingEnumResolver SINGLE_ENUM_RESOLVER = new CachingEnumResolver ();
/* MSGCODE-> Category memory Index */
Private static Map CODE_MAP_CACHE;
Static {
CODE_MAP_CACHE = new HashMap ();
// To illustrate the problem, I initialize a piece of data here
CODE_MAP_CACHE.put ("0", "Beijing ");
// Private, for single instance
Private CachingEnumResolver (){
// This method is also responsible for initialization and loading of data
InitEnums ();
/*** Initialize all enumeration types */
Public static void initEnums (){
//~~~~~~~~~ The problem is exposed from here ~~~~~~~~~~~ //
If (null = CODE_MAP_CACHE ){
System. out. println ("CODE_MAP_CACHE is empty. The problem is exposed here .");
CODE_MAP_CACHE = new HashMap ();
CODE_MAP_CACHE.put ("1", "Beijing ");
CODE_MAP_CACHE.put ("2", "Yunnan Province ");
//... Other code...
Public Map getCache (){
Return Collections. unmodifiableMap (CODE_MAP_CACHE );
/*** Get a single-State instance *** @ return */
Public static CachingEnumResolver getInstance (){
Public static void main (String [] args ){
System. out. println (CachingEnumResolver. getInstance (). getCache ());

After reading the above Code, you may feel a little confused. This class seems to be okay. This is indeed a typical hungry Chinese single-State mode. How can this problem be solved?

Yes, it seems that there is no problem, but if you run him up, the result is that he will not work correctly for you. Run this class. The execution result is:
[List 2]

CODE_MAP_CACHE is empty. The problem is exposed here. {0 = Beijing}

How is my program like this? Why is CODE_MAP_CACHE empty in the initEnum () method? Why does the output CODE_MAP_CACHE contain only one element and the other two elements ????!!

If you are debugging the program, you must be surprised at the moment. Is there a problem with my Jvm? None! If not, what's wrong with my program? This is definitely not the result I want. In fact, no matter how you modify the initEnum () method, at least I will not doubt that the problem may occur when creating a CachingEnumResolver instance. It is precisely because I believe in the method for creating a CachingEnumResolver instance, coupled with a misunderstanding of the underlying principles of Java class initialization and Object Instantiation, it took me three or four hours-about half a working day.

So what exactly is the problem? Why is there such a strange thing? Before solving this problem, let's take a look at the underlying mechanism of JVM classes and object initialization.

Class Lifecycle

The figure shows the flow of the class life cycle. In this article, I only want to talk about the two stages of Class "initialization" and "Object Instantiation.

Class initialization

Class "initialization" stage, which is the last task of a class or interface before it is used for the first time. This stage is responsible for assigning correct initial values to class variables.

The Java compiler collects all the class variable initialization statements and static class initiators into the method. This method can only be called by Jvm and is dedicated to initialization.

In addition to interfaces, before initializing a class, you must ensure that its direct superclass has been initialized, and the initialization process is thread-safe by Jvm. In addition, not all classes have a () method, and the class does not have a () method in the following conditions:

This class neither declares any class variables nor static initialization statements; this class declares class variables, but does not explicitly use class variable initialization statements or static initialization statements for initialization; this class only contains the class variable initialization Statement of static final variables, and the class variable initialization statement is a regular expression of compilation.
Object initialization

When a class is loaded, connected, and initialized, this class can be used at any time. Object Instantiation and initialization are the activities of the initial stage of object life. Here we mainly discuss the characteristics of object initialization.

When compiling each class, the Java compiler generates at least one instance Initialization Method for the Class-that is, the "()" method. This method corresponds to each constructor in the source code. If the class does not explicitly declare any constructor, the compiler generates a default non-argument constructor for the class, the default constructor only calls the parameter-free constructor of the parent class, and generates a "()" method corresponding to the default constructor.

Generally, the Code contained in the () method is like calling another () method, initializing instance variables, and the code in the corresponding constructor. If the constructor is explicitly starting from calling another constructor in the same class, the corresponding () method contains the following content: A Call to the () method of this class; all bytecode in the application constructor.

If the constructor does not start by calling other constructor methods of its own class and the Object is not an Object, the content contained in the () method is: a pair of parent class () method call; the bytecode of the initialization method for instance variables; finally, the bytecode corresponding to the Construction of sub-methods.

If this class is an Object, its () method does not include calls to the parent class () method.

Class initialization time

So far, we have learned about the stages of the class lifecycle, but when is the class loading triggered at the beginning of the class lifecycle? When is the class initialized? Let's continue searching for answers with these three questions.

The Java virtual machine specification strictly defines the class initialization time: "initialize on first active use" -- "Initializing upon first active use ". This rule directly affects the mechanism of class loading, connection, and initialization classes-because it must have been connected before the type is initialized, but it must have been loaded before the connection.

The Java virtual machine specification does not strictly define the class loading time related to the initialization time, this allows the JVM to adopt different loading policies based on its own characteristics. Let's take a look at the implementation principle of the Jboss AOP framework, which is to put your hands and feet on the loading of your class file-insert the relevant interception bytecode of AOP, this makes it completely transparent to programmers. Even the object instances you create with the new operator can be intercepted by the AOP framework-the corresponding Spring AOP, you must use his BeanFactory to obtain the managed objects that have been proxies by AOP. Of course, the disadvantage of Jboss AOP is also obvious-he is closely bound to the JBOSS server, you cannot easily port data to other servers. Hmm ~......, Speaking of this, I have some questions. I need to know that I can write a thick book on the implementation strategy of AOP. Hey hey, I can't help it.

After talking about this, the class initialization time is "when it is used for the first time". Under what circumstances does it meet the requirements for the first active use?

First active use:

· New, reflection, cloning, or deserialization when creating a new instance of a class;

· When calling a static method of a class;

· When a static field of a class or interface is used or the field is assigned a value (except for final fields );

· When calling some Java reflection methods

· When initializing a subclass of A Class

· The startup class that contains the main () method when the VM is started.

Except for the above situations, all other methods that use the JAVA type are passively used, and they will not cause class initialization.

Where exactly is my problem?

Now that we understand the JVM class initialization and object initialization mechanisms, we have a theoretical basis to rationally analyze the problem.

Next, let's take a look at the bytecode translated from the JAVA source code anti-group in [LIST 1:

[List 3]

Public class com. ccb. framework. enums. CachingEnumResolver extendsjava. lang. Object {
Static {};
Code: 0: new #2;
// Class CachingEnumResolver
3: dup
4: invokespecial #14;
// Method "" :() V ①
7: putstatic #16;
// Field SINGLE_ENUM_RESOLVER: Lcom/ccb/framework/enums/CachingEnumResolver;
10: new #18;
// Class HashMap ②
13: dup
14: invokespecial #19;
// Method java/util/HashMap. "" :() V
17: putstatic #21;
// Field CODE_MAP_CACHE: Ljava/util/Map;
20: getstatic #21;
// Field CODE_MAP_CACHE: Ljava/util/Map;
23: ldc #23;
// String 0
25: ldc #25;
// String Beijing
27: invokeinterface #31, 3;
// InterfaceMethod java/util/Map. put :( Ljava/lang/Object;) Ljava/lang/Object; ③
32: pop 33: returnprivate com. ccb. framework. enums. CachingEnumResolver ();
Code: 0: aload_0 1: invokespecial #34;
// Method java/lang/Object. "" :() V 4: invokestatic #37;
// Method initEnums :() V ④ 7: returnpublic static void initEnums ();
Code: 0: getstatic #21;
// Field CODE_MAP_CACHE: Ljava/util/Map;
⑤ 3: ifnonnull 24 6: getstatic #44;
// Field java/lang/System. out: Ljava/io/PrintStream;
9: ldc #46;
// String CODE_MAP_CACHE is empty. The problem is exposed here.
11: invokevirtual #52;
// Method java/io/PrintStream. println :( Ljava/lang/String;) V 14: new #18;
// Class HashMap 17: dup 18: invokespecial #19;
// Method java/util/HashMap. "" :() V ⑥ 21: putstatic #21;
// Field CODE_MAP_CACHE: Ljava/util/Map;
24: getstatic #21;
// Field CODE_MAP_CACHE: Ljava/util/Map;
27: ldc #54;
// String 1 29: ldc #25;
// String Beijing 31: invokeinterface #31, 3;
// InterfaceMethod java/util/Map. put :( Ljava/lang/Object;
Ljava/lang/Object;) Ljava/lang/Object;
736: pop 37: getstatic #21;
// Field CODE_MAP_CACHE: Ljava/util/Map;
40: ldc #56;
// String 2 42: ldc #58;
// String Yunnan 44: invokeinterface #31, 3;
// InterfaceMethod java/util/Map. put :( Ljava/lang/Object;) Ljava/lang/Object;
Listen 49: pop 50: returnpublic java. util. Map getCache ();
Code: 0: getstatic #21;
// Field CODE_MAP_CACHE: Ljava/util/Map;
3: invokestatic #66;
// Method java/util/Collections. unmodifiableMap :( Ljava/util/Map;) Ljava/util/Map;
6: areturnpublic static com. ccb. framework. enums. CachingEnumResolver getInstance ();
Code: 0: getstatic #16;
// Field SINGLE_ENUM_RESOLVER: Lcom/ccb/framework/enums/CachingEnumResolver;
Required 3: areturn}
If [LIST 1] shows that the List contains bytecode in the JDK1.4 environment, this list may not be very attractive to many of the brothers, because these JVM commands are really not as easy to understand as the source code. But it is indeed the most direct way to find and locate the problem. The answer we want is in this JVM command list.

Now, let's analyze the code execution track in [LIST 1] from class initialization to object instance initialization.

As described above, class initialization is the last step of the previous work when the class is actually available. This stage is responsible for correct initialization values for all classes, which is thread-safe, JVM ensures multi-thread synchronization.

Step 2: Call the class initialization method CachingEnumResolver. (). This method is invisible to the outside world. In other words, it is a dedicated internal JVM method. () It includes all the initialization statements of class variables with the specified initial values in CachingEnumResolver. Note that not every class has this method. The specific content is described earlier.

Step 2: Enter the () method. Let's look at the "1" line in the bytecode. This line is combined with the above two lines to represent a new CachingEnumResolver object instance, the code line itself refers to calling the () method of the CachingEnumResolver class. Each Java class has a () method, which is generated by the Java compiler during compilation and invisible to the outside world ,() the method includes all instance variable initialization statements with the specified initialization value and all statements in the java class constructor. This method is used to initialize objects during instantiation. However, at this point, a potential problem has been waiting for you to commit a crime.

Step 2: Let's look down the execution order. For the "④" Row, the method of this row is the constructor of this class. This method first calls the constructor of the parent class () initialize the parent object and call CachingEnumResolver. initEnum () method to load data.

Step 2: "⑤". This row obtains the "CODE_MAP_CACHE" field value, which is null during running. Note that the problem has started to appear. (As a programmer, you must have hoped that the field has been initialized, but in fact it has not been initialized ). By judging, because the field is NULL, the program will continue to execute the "6" Row and instantiate the field as HashMap ().

Step 2: In the "7" and "Hangzhou" lines, the function is to fill in two pieces of data for the "CODE_MAP_CACHE" field.

Step 2: exit the object initialization method () and initialize the generated object instance to the class field "SINGLE_ENUM_RESOLVER ". (Note: At this moment, the class variables in the object instance have not been fully initialized. The class variable "CODE_MAP_CACHE" assigned by the () method called initEnum () method is not initialized yet, it will be overwritten again in the subsequent class initialization process ).

Step 2: continue to execute the subsequent code in the () method. Line 2: instantiate the "CODE_MAP_CACHE" field as a HashMap instance (note: this field has been assigned a value during Object Instantiation and is now assigned to another instance. At this moment, the class variable value of the Instance referenced by the "CODE_MAP_CACHE" variable is overwritten, at this point, our questions have been answered ).

Step 2: The class initialization is complete, and the single-State class instantiation is also completed.

Through the above bytecode Execution Process Analysis, you may have understood the underlying cause of the error, or you may have been confused by the above analysis process, however, the problem is not broken. Although I can also elaborate on the problem from the source code perspective, it is not deep enough, and it is only possible for my personal opinion and lack of credibility.


To solve the problem in the above Code, it is very easy to transfer the initialization value assignment statement of the "SINGLE_ENUM_RESOLVER" variable to the getInstance () method. In other words, you must avoid instantiating the class from the inside or referencing fields that have not been initialized before the class initialization is complete.


Calm down and cool, and carefully think about whether you have mastered the knowledge of the topic in this article. If you think you have mastered it completely or basically, it is very good. At the end, I will slightly modify the previous Code. Do you have any problems with the two groups of programs?

Procedure 1

Public class CachingEnumResolver {
Public static Map CODE_MAP_CACHE;
Static {
CODE_MAP_CACHE = new HashMap ();
// To illustrate the problem, I initialize a piece of data here
CODE_MAP_CACHE.put ("0", "Beijing ");
InitEnums ();

Procedure 2

Public class CachingEnumResolver {
Private static final CachingEnumResolver SINGLE_ENUM_RESOLVER;
Public static Map CODE_MAP_CACHE;
Static {
CODE_MAP_CACHE = new HashMap ();
// To illustrate the problem, I initialize a piece of data here
CODE_MAP_CACHE.put ("0", "Beijing ");
SINGLE_ENUM_RESOLVER = new CachingEnumResolver ();
InitEnums ();

Finally, some comments about the JAVA Community: Spring is a popular open-source framework, attracting the attention of a large number of JEE developers (I am also a member of fans ). However, let's take a closer look. How many of Spring fans have studied Spring source code? How many people have a deep understanding of Spring design ideas? Of course, I am not qualified to talk about things in such a tone. I just want to express my point of view that learning things must be "Clear the source ".
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.