Java memory allocation and management are one of the core technologies of Java. We have previously introduced the knowledge of Java's memory management, memory leakage and Java garbage collection. Today, we will once again go deep into the Java core and introduce in detail the knowledge of Java in memory allocation. Generally, Java will involve the following areas when allocating memory:
◆Register: We cannot control it in the program
◆Stack: Stores basic types of data and references to objects, but the object itself is not stored in the stack, but is stored in the heap (the object that comes out of new)
◆Heap: Store data generated using new
◆ Static domain: static members stored in an object defined with static
◆Constant pool: store constants
◆Non-RAM storage: permanent storage space such as hard disk
Stack in Java memory allocation
Some basic types of variable data defined in the function and reference variables of the object are all allocated in the function's stack memory.
When a variable is defined in a block of code, Java allocates memory space for the variable in the stack. When the variable exits the scope, Java will automatically release the memory space allocated for the variable, and the memory space can be used separately immediately. The data size and life cycle in the stack are certain, and this data disappears when no reference points to the data.
Heap in Java memory allocation
Heap memory is used to store objects and arrays created by new. The memory allocated in the heap is managed by the Java virtual machine's automatic garbage collector.
After an array or object is generated in the heap, a special variable can be defined in the stack, so that the value of this variable in the stack is equal to the first address of the array or object in the heap memory, and the variable in the stack becomes a reference variable for the array or object. A reference variable is equivalent to a name given to an array or object. You can use the reference variables in the stack in the program to access the array or object in the heap. A reference variable is equivalent to a name given to an array or object.
Reference variables are ordinary variables, which are assigned on the stack when defined. Reference variables are released after the program runs outside its scope. The array and object themselves are allocated in the heap. Even if the program runs outside the code block where the statements that use new to generate the array or object are located, the memory occupied by the array and object itself will not be released. The array and object only become garbage when there is no reference variable pointing to it, and cannot be used, but still occupy the memory space. It is collected (released) by the garbage collector at an uncertain time. This is also the reason why Java takes up more memory.
In fact, variables in the stack point to variables in heap memory, which is the pointer in Java!
Heap and stack
Java's heap is a runtime data area, from which objects allocate space. These objects are established through instructions such as new, newarray, anewarray and multianewarray. They do not require program code to be explicitly released. The heap is responsible for garbage collection. The advantage of the heap is that it can dynamically allocate the memory size, and the lifetime does not have to be told to the compiler in advance, because it dynamically allocates memory at runtime. Java's garbage collector will automatically collect the data that is no longer used. But the disadvantage is that because it needs to dynamically allocate memory at runtime, the access speed is slower.
The advantage of the stack is that the access speed is faster than the heap, second only to the registers, and the stack data can be shared. But the disadvantage is that the data size and lifetime in the stack must be deterministic and lack flexibility. The stack mainly stores some basic types of variable data (int, short, long, byte, float, double, boolean, char) and object handles (references).
A very important special feature of the stack is that the data existing in the stack can be shared. Suppose we define at the same time:
Java code
int a = 3;
int b = 3;
The compiler first processes int a = 3; first it will create a reference in the stack with a variable a, and then find out whether there is a value of 3 in the stack. If it is not found, it will store 3 and then point a to 3. Then process int b = 3; after creating the reference variable of b, because there is already a value of 3 in the stack, b is directly pointed to 3. In this way, a and b both point to 3 at the same time.
At this time, if a=4 is set again; then the compiler will search again for whether there is a 4 value in the stack. If not, store 4 and point a to 4; if it already exists, point a to this address directly. Therefore, the change in value a will not affect the value b.
It should be noted that this sharing of data is different from the sharing of references from two objects pointing to one object at the same time, because in this case the modification of a will not affect b, it is done by the compiler, which is conducive to saving space. An object reference variable modifies the internal state of this object and will affect another object reference variable.
Java code
1.int i1 = 9;
2.int i2 = 9;
3.int i3 = 9;
4.public static final int INT1 = 9;
5.public static final int INT2 = 9;
6.public static final int INT3 = 9;
For member variables and local variables: member variables are variables defined inside the method and the class; local variables are variables defined inside the method or statement block. Local variables must be initialized.
Formal parameters are local variables, and the data of local variables exists in the stack memory. Local variables in the stack memory disappear as the method disappears.
Member variables are stored in objects in the heap and are collected by the garbage collector.
As in the following code:
Java code
class BirthDate { private int day; private int month; private int year; public BirthDate(int d, int m, int y) { day = d; month = m; year = y; } omit get,set method…… } public class Test{ public static void main(String args[]){ int date = 9; Test test = new Test(); test.change(date); BirthDate d1= new BirthDate(7,7,1970); } public void change1(int i){ i = 1234; }For the above code, date is a local variable, i, d, m, y are all formal parameters as local variables, and day, month, and year are member variables. Let’s analyze the changes during code execution:
1. The main method starts executing: int date = 9;
date local variables, basic types, references and values are all present on the stack.
2. Test test = new Test();
test is an object reference, it exists on the stack, and the object (new Test()) exists on the heap.
3. test.change(date);
i is a local variable, and the reference and value are present in the stack. When the method change is executed, i will disappear from the stack.
4. BirthDate d1= new BirthDate(7,7,1970);
d1 is an object reference and exists in the stack. Objects (new BirthDate()) exist in the heap, where d, m, y are local variables stored in the stack, and their types are the base types, so their data is also stored in the stack. day, month, year are member variables, and they are stored in the heap (new BirthDate()). When the BirthDate constructor is executed, d,m,y will disappear from the stack.
5. After the main method is executed, the date variable, test, and d1 reference will disappear from the stack, and new Test(), new BirthDate() will wait for garbage collection.
Constant pool
Constant pools refer to some data that is determined during the compilation period and is saved in the compiled .class file.
In addition to containing the constant values (final) of various basic types (such as int, long, etc.) and object types (such as String and arrays) defined in the code, it also contains some symbolic references in text form, such as:
◆The fully qualified names of classes and interfaces;
◆The name and descriptor of the field;
◆Methods and names and descriptors.
If the compile period has been created (defined directly in double quotes), it will be stored in the constant pool, and if it can be determined by the run period (from new), it will be stored in the heap. For strings with equals, there is always only one copy in the constant pool and multiple copy in the heap.
String is a special packaging data. Can be used:
Java code
String str = new String("abc");String str = "abc";There are two forms to create. The first is to use new() to create a new object, which will be stored in the heap. A new object is created every time it is called. The second type is to first create a variable str to the object of the String class in the stack, and then use symbolic reference to find out if there is "abc" in the string constant pool. If not, store "abc" into the string constant pool and let str point to "abc". If there is already "abc", then directly let str point to "abc".
When comparing whether the values in the class are equal, use the equals() method; when testing whether the references of the two wrapper classes point to the same object, use ==, and use the example below to illustrate the above theory.
Java code
String str1 = "abc";String str2 = "abc";System.out.println(str1==str2); //true
It can be seen that str1 and str2 point to the same object.
Java code
String str1 =new String ("abc");String str2 =new String ("abc");System.out.println(str1==str2); // falseThe new method is to generate different objects. Generate one at a time.
Therefore, in the second way, multiple "abc" strings are created, and there is only one object in memory. This writing method is beneficial and saves memory space. At the same time, it can improve the running speed of the program to a certain extent, because the JVM will automatically decide whether it is necessary to create a new object based on the actual situation of the data in the stack. For the code of String str = new String("abc");, new objects are created in the heap regardless of whether their string values are equal or not, whether it is necessary to create new objects, thereby increasing the burden on the program.
On the other hand, note: When we define a class using a format such as String str = "abc";, we always take it for granted that we create an object str of the String class. Worry about the trap! The object may not have been created! And maybe just point to an object that has been created previously. Only through the new() method can we ensure that a new object is created every time.
Several examples of String constant pooling problem
Example 1:
Java code
String s0="kvill";String s1="kvill";String s2="kv" + "ill";System.out.println( s0==s1 );System.out.println( s0==s2 ); The result is: truetrue
Analysis: First of all, we need to know that the result is Java will ensure that a string constant has only one copy.
Because s0 and s1 in the example are both string constants, they are determined during the compilation period, so s0==s1 is true; and "kv" and "ill" are also string constants. When a string is connected by multiple string constants, it is definitely also a string constant, so s2 is also parsed into a string constant during the compilation period, so s2 is also a reference to "kvill" in the constant pool. So we get s0==s1==s2;
Example 2:
Example:
Java code
Analysis: The strings created with new String() are not constants and cannot be determined during the compilation period, so the strings created by new String() are not placed in the constant pool, they have their own address space.
s0 is also an application of "kvill" in the constant pool. s1 cannot be determined during the compilation period, so it is a reference to the new object "kvill" created at runtime. s2 cannot be determined during the compilation period because it has the second half of new String("ill"), so it is also an application of the newly created object "kvill"; if you understand these, you will know why this result is obtained.
Example 3:
Java code
String a = "a1";String b = "a" + 1;System.out.println((a == b)); //result = true String a = "true";String b = "a" + "true";System.out.println((a == b)); //result = true String a = "a3.4";String b = "a" + 3.4;System.out.println((a == b)); //result = true
Analysis: For the JVM connection of string constants, the JVM optimizes the "+" connection of the constant string to the connected value after the program compilation period. Take "a" + 1 as an example. After optimization by the compiler, it is already a1 in the class. During the compilation period, the value of the string constant is determined, so the final result of the above program is true.
Example 4:
Java code
String a = "ab";String bb = "b";String b = "a" + bb;System.out.println((a == b)); //result = false
Analysis: For string references in JVM, since there are string references in the "+" connection of strings, the referenced value cannot be determined during the program compilation period, that is, "a" + bb cannot be optimized by the compiler, and only dynamically allocates and assigns the connected new address to b during the program run period. Therefore, the result of the above program is false.
Example 5:
Java code
String a = "ab";final String bb = "b";String b = "a" + bb;System.out.println((a == b)); //result = true
Analysis: The only difference between [4] is that the bb string is decorated with final modification. For final modified variables, it is parsed as a local copy of the constant value at compile time and stored in its own constant pool or embedded in its bytecode stream. So at this time the effects of "a" + bb and "a" + "b" are the same. Therefore, the result of the above program is true.
Example 6:
Java code
String a = "ab";final String bb = getBB();String b = "a" + bb;System.out.println((a == b)); //result = falseprivate static String getBB() { return "b"; }Analysis: The JVM references bb for strings, and its value cannot be determined during the compilation period. Only after calling the method during the program runtime, the return value of the method and "a" are dynamically connected and the address is assigned to b. Therefore, the result of the above program is false.
About String is immutable
From the above example, we can find out:
String s = "a" + "b" + "c";
It is equivalent to String s = "abc";
String a = "a";
String b = "b";
String c = "c";
String s = a + b + c;
This is different, the final result is equal to:
Java code
StringBuffer temp = new StringBuffer();temp.append(a).append(b).append(c);String s = temp.toString();
From the above analysis results, it is not difficult to infer that String uses the connection operator (+) to analyze the reason for the inefficiency, like this code:
Java code
public class Test { public static void main(String args[]) { String s = null; for(int i = 0; i < 100; i++) { s += "a"; } }}Every time + is done, a StringBuilder object is generated, and then appends it and throws it away. The next time the loop arrives, a StringBuilder object is regenerated, and then append the string, and the loop is completed until it ends. If we directly use the StringBuilder object to append, we can save N - 1 time to create and destroy the object. Therefore, for applications that require string concatenation in a loop, the append operation is generally performed using StringBuffer or StringBulider objects.
Due to the immutable nature of the String class, there is a lot to say about this. As long as you know that the instance of String will not change once it is generated, for example: String str=”kv”+”ill”+” “+”ans”; there are 4 string constants, first “kv” and “ill” generate “kvill” in memory, and then “kvill” and “”” generate “kvill” and “” and finally generate “kvill ans”; and assign the address of this string to str, because String’s “immutable” generates many temporary variables, which is why it is recommended to use StringBuffer, because StringBuffer is changeable.
Final usage and understanding in String
Java code
final StringBuffer a = new StringBuffer("111");final StringBuffer b = new StringBuffer("222");a=b;//This sentence does not compile until it is finished. final StringBuffer a = new StringBuffer("111");a.append("222");/// Compile after it is finishedIt can be seen that final is only valid for the referenced "value" (i.e., memory address). It forces the reference to only point to the object that was initially pointed to. Changing its pointing will cause a compile-time error. As for the changes in the object it points to, final is irresponsible.
Summarize
The stack is used to store some local variable data of the original data type and references to objects (String, array, object, etc.) but does not store object content
Objects created using the new keyword are stored in the heap.
A string is a special wrapper class, and its references are stored in the stack, and the object content must be determined according to the creation method (constant pool and heap). Some are created at the compile time and stored in the string constant pool, while others are created only at runtime. Use the new keyword and stored in the heap.
The above article briefly talks about the difference between Java+ memory allocation and variable storage location is all the content I share with you. I hope you can give you a reference and I hope you can support Wulin.com more.