This article will introduce the principle of Java memory allocation in detail, to help novice easier to learn Java. There are many such articles online, but most of them are fragmentary. From the perspective of cognitive process, this paper will bring readers a systematic introduction.
This article reprinted since the attacks on the Chinese great god’s blog, the original links: blog.csdn.net/shimiso/art…
The first thing to know before we get to the topic is that Java programs run on the JVM(Java Virtual Machine, Java Virtual Machine), can be understood as a bridge between Java programs and the operating system, JVM to achieve Java platform independent, so we can see the importance of THE JVM. So when learning the principles of Java memory allocation, it is important to keep in mind that all of this is done in the JVM, which is the basis and premise of the principle of memory allocation.
In simple terms, a complete Java program execution involves the following memory areas:
L register: JVM internal virtual register, access speed is very fast, the program can not control.
L stack: store values of local variables, including: 1. Store values of basic data types; 2. 2. Save an instance of the class, a reference (pointer) to the heap object. It can also be used to save frames from loading methods.
L heap: Used to store dynamically generated data, such as new objects. Note that the created objects contain only their own member variables, not member methods. Because objects of the same class have their own member variables, stored in their own heap, but they share the class’s methods, they are not copied every time an object is created.
L Constant pool: The JVM maintains a constant pool for each loaded type, which is an ordered collection of constants used by that type. This includes direct constants (primitive types, strings) and symbolic references to other types, methods, and fields (1). The data in the pool is accessed by an index just like an array. Constant pools play a central role in Java dynamic linking because they contain all symbolic references to other types, methods, and fields of a type. Constant pools exist in the heap.
L Code snippet: Used to store source code read from the hard disk.
L Data segment: used to store static members defined static.
Here is the memory representation:
The figure above gives a general description of Java memory allocation. Next, we will explain in detail how Java programs run in memory through examples. (Note: the following picture is taken from the J2SE courseware of Ma Junjun in School.
Preliminary knowledge:
1. A Java file with a main entry method is considered a Java program that can be compiled and run separately.
2. Variables of common type and variables of reference type (commonly known as instances) can be used as local variables, and they can all appear on the stack. Whereas a regular variable holds its value on the stack, a reference variable holds a pointer to the heap, which can be used to find the object corresponding to the instance in the heap. Therefore, ordinary type variables occupy only one block of memory in the stack area, while reference type variables occupy one block of memory in the stack area and one in the heap area.
Example:
支那
支那
1. The JVM automatically looks for the main method, executes the first line of code, creates an instance of the Test class, and allocates a block of memory on the stack to hold a pointer to the heap object 110925.
2. Create a variable of type date, which is a basic type, and place the corresponding value 9 on the stack.
3. Create two BirthDate instances D1 and d2, and store Pointers to the BirthDate objects on the stack. They call the constructor with arguments at instantiation, so the object has a custom initial value.
\
Call the Change1 method of the test object, taking date. When the JVM reads this code, it detects that I is a local variable, so it puts I on the stack and assigns the value of date to I.
\
I assign 1234 to I. It’s an easy step.
\
Immediately after the change1 method completes, the stack space occupied by the local variable I is released.
\
\
Call the change2 method of the test object, taking instance d1 as argument. The JVM detects that the parameter b in the change2 method is a local variable and immediately adds it to the stack. Since it is a reference type variable, b holds a pointer to d1, which points to the same object in the heap as D1. Passing between B and d1 is a pointer.
\
\
A BirthDate object is instantiated in the Change2 method and assigned to B. Internally, a new object is created in the heap, and the pointer to the object is stored in the corresponding space of B on the stack. At this time, instance B no longer points to the object pointed to by instance D1, but the object pointed to by instance D1 does not change, so d1 will not be affected.
\
When the change2 method is executed, the stack space occupied by the local reference variable B is released immediately. Note that only the stack space is freed, and the heap space is waiting for automatic reclamation.
\
Call the Change3 method of the test instance, taking instance d2 as an argument. Similarly, the JVM allocates space on the stack for a locally referenced variable b, and places a pointer to D2 in B, where D2 and B point to the same object. Call the setDay method of instance B, which is the setDay method of the object pointed to by d2.
\
Calling the setDay method of instance B affects D2 because they refer to the same object.
\
Immediately after the change3 method is executed, the local reference variable B is released.
This is the general picture of memory allocation at runtime for Java programs. In fact, there is nothing, grasp the idea is very simple. There are only two types of variables: primary and reference. As local variables, both are placed on the stack. Primitive types hold their values directly on the stack, and reference types hold only a pointer to the heap, where the real objects are stored. When used as arguments, primitive types pass values directly, and reference types pass Pointers.
Summary:
1. Define what is an instance and what is an object. Class a= new Class(); A is called an instance, not an object. The instance is in the stack, the object is in the heap, and manipulating the instance is actually manipulating the object indirectly through the pointer to the instance. Multiple instances can point to the same object.
2. Stack data and heap data destruction are not synchronized. Once the method ends, local variables in the stack are destroyed, but objects in the heap are not necessarily destroyed. Because there may be other variables pointing to the object, it is not destroyed until there are no variables in the stack pointing to objects in the heap, and it is not destroyed immediately until the garbage collection is scanned.
3. The above stack, heap, code segment, data segment, and so on are all relative to the application. Each application corresponds to a unique JVM instance, and each JVM instance has its own memory area, which does not affect each other. And these memory areas are shared by all threads. The stack and heap mentioned here are holistic concepts, and these stacks can be subdivided.
4. Member variables of a class vary from object to object and each has its own storage space (member variables are in objects in the heap). The method of the class is shared by all objects of the class. There is only one set of methods. When the object uses the method, the method is pushed onto the stack.
This analysis covers only stacks and heaps, but there is one very important area of memory that can cause some puzzling problems: the constant pool. What a constant pool does has been explained above, and it doesn’t need to be understood too deeply, just remember that it maintains a constant of a loaded class. The following examples illustrate the properties of constant pools.
Preliminary knowledge:
Primitive types and wrapper classes for primitive types. The basic types are byte, short, char, int, long, and Boolean. The wrapper classes for the basic types are Byte, Short, Character, Integer, Long, and Boolean. Be case sensitive. The difference between the two is that the basic type is a common variable in the program, and the wrapper class of the basic type is a class, which is a reference variable in the program. So they are stored differently in memory: the base type is stored in the stack, while the base type wrapper class is stored in the heap. The above mentioned wrapper classes all implement constant pooling technology, while the other two floating-point wrapper classes do not. In addition, String implements constant pool technology.
\
Example:
1 public class test { 2 public static void main(String[] args) { 3 objPoolTest(); 4 } 5 6 public static void objPoolTest() { 7 int i = 40; 8 int i0 = 40; 9 Integer i1 = 40; 10 Integer i2 = 40; 11 Integer i3 = 0; 12 Integer i4 = new Integer(40); 13 Integer i5 = new Integer(40); 14 Integer i6 = new Integer(0); 15 Double d1 = 1.0; 16 Double d2 = 1.0; 17 18 System.out.println("i=i0\t" + (i == i0)); 19 System.out.println("i1=i2\t" + (i1 == i2)); 20 System.out.println("i1=i2+i3\t" + (i1 == i2 + i3)); 21 System.out.println("i4=i5\t" + (i4 == i5)); 22 System.out.println("i4=i5+i6\t" + (i4 == i5 + i6)); 23 System.out.println("d1=d2\t" + (d1==d2)); 24 25 System.out.println(); 26}} 27Copy the code
Results:
1 i=i0 true
2 i1=i2 true
3 i1=i2+i3 true
4 i4=i5 false
5 i4=i5+i6 true
6 d1=d2 false
Copy the code
Result analysis ** : **
1. Both I and i0 are variables of common type (int), so the data is stored directly in the stack, and the stack has an important feature: the data in the stack can be shared. When we define int I = 40; Int i0 = 40; The stack is automatically checked to see if 40 is present, and if so, i0 points directly to 40 of I, without adding a new 40.
2. I1 and i2 are reference types and store Pointers on the stack because Integer is a wrapper class. Since the Integer wrapper class implements constant pooling technology, the 40 values for i1 and i2 are both taken from the constant pool and point to the same address, so i1=12.
Java does all the math on the stack. Java automatically unboxes i1 and i2 into integers, so i1 is equal to i2+i3.
4. I4 and i5 are reference types and store Pointers on the stack because Integer is a wrapper class. But since they’re both new, instead of looking for data from the constant pool, they’re each going to new an object from the heap, and then they’re going to hold a pointer to that object, so i4 and I5 are not equal, because they have different Pointers to different objects.
5. This is also an addition operation, the same as 3.
6. Both d1 and d2 are reference types and store Pointers on the stack because Double is a wrapper class. But the Double wrapper class does not implement constant pool technology, so Doubled1=1.0; Double d1=new Double(1.0); , new an object from the heap, same with d2. So d1 and D2 hold different Pointers and point to different objects, so they are not equal.
Summary:
1. The basic type wrapper classes mentioned above all implement constant pooling technology, but the constants they maintain are only in the range [-128 to 127]. If constant values exceed this range, objects are created from the heap, not from the constant pool. For example, change the above example to Integer i1 = 400; Integer i2 = 400; If you can’t get a constant from the constant pool, you have to get a new Integer object from the heap, and i1 and i2 are no longer equal.
2. The String type also implements the constant pool technique, but with a slight difference. The String type checks whether there is a corresponding String in the constant pool. If not, add the current one.
Those involving the principle of memory are generally extensive and profound fields, do not listen to a family of words, read more articles. I am only in this analysis, there are a lot of tricks, left to the reader to explore thinking. Hope this article can be helpful to everyone!
Footnote:
(1) Symbolic reference, as the name implies, is a symbol, the symbol will be resolved only when the symbol reference is used. If you are familiar with Linux or Unix systems, you can think of this symbolic reference as a soft link to a file. When you use this soft link, you actually parse it and expand it to find the actual file
Symbolic references are mostly discussed at the class-loading level, but the source level is a formal discussion.
When a class is loaded, symbolic references to other classes used by that class are stored in the constant pool. When the actual code executes, the JVM expands the symbolic references to that class in the constant pool to a direct reference, so that the JVM does not parse them the next time it encounters the same type. Instead, use the direct reference that has been parsed.
For example, system.out.println (“test” +” ABC “); system.out.println (“test” +” ABC “); // What happens here is equivalent to a direct reference, but suppose some Strings = “ABC “; System.out.println(“test” + s); // What happens here is equivalent to a symbolic reference, that is, expanding s as a symbolic link to “ABC”, that is, the class file does not display s directly at compile time, but instead sees the s as a symbol, which will be expanded when the actual code executes.