This section documents some of the more in-depth aspects of JVM interviews
Static Block of static code and static variables which are assigned first
This is a hard question to answer, but you may not understand what I’m talking about, but take a look at the code
public class Max {
static {
staticIntValue = 300;
}
public static int staticIntValue = 100;
public final int finalIntValue = 3;
public int intValue = 1;
public static void main(String[] args) {
try {
Thread.sleep(100000);
} catch(InterruptedException e) { e.printStackTrace(); }}}Copy the code
[q:]
The question is what staticIntValue is,【答案:】
staticIntValue=100
I will say the answer first, for you can think, this problem in several training institutions in the open class have seen, but it is difficult to say in place. There is a fundamental reason for all tasks and events in this world. Some of them seem to be unconstrained, but in fact, we don’t have such a fast knowledge system, so we don’t know how to start
So how to analyze this problem? This is Java code, Java code is compiled into bytecode, how to assign values, the order of assignment is actually determined at compile time, we decompile bytecode clean, okay
Static attributes are allocated during the validator phase of class loading. This phase gives the static attribute a default value, and then combines the assignment of the static attribute with the static code block to generate a class initialization method. In the same order as the code is written. Static is written last
Look at the static code above
public class Max {
static {
staticIntValue = 300;
}
public static int staticIntValue = 100;
}
Copy the code
Decompiled bytecode
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: sipush 300
3: putstatic #9 // Field staticIntValue:I
6: bipush 100
8: putstatic #9 // Field staticIntValue:I
11: return
LineNumberTable:
line 11: 0
line 14: 6
}
Copy the code
StaticIntValue Specifies the memory address, default 0, 300, and 100
Let’s switch the order, the static block comes after
public class Max {
public static int staticIntValue = 100;
static {
staticIntValue = 300; }}Copy the code
Decompiled bytecode
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: bipush 100
2: putstatic #9 // Field staticIntValue:I
5: sipush 300
8: putstatic #9 // Field staticIntValue:I
11: return
LineNumberTable:
line 10: 0
line 13: 5
line 14: 11
}
Copy the code
StaticIntValue Opens the memory address, defaults to 0, then 100, then 300
In fact, it is very simple, as long as we find the corresponding knowledge system of the problem, all problems are not so difficult
Where is static stored
String name = new String(“AA”); Static = = where are the left and right sides of the static variable
Look at the code:
public class Max {
public static byte[] values = new byte[1024*1024*60];
public static void main(String[] args) {
System.out.println("values: "+values); }}Copy the code
Let’s first look at the new part on the right of the = sign: the method is simple, just print out the stack information, in the code we have a variable of 60M
PSYoungGen Total 38400K, used 4663K [0x0000000795580000, 0x0000000798000000, PSYoungGen Total 38400K, used 4663K [0x0000000795580000, 0x0000000798000000 0x00000007c0000000) eden space 33280K, 14%, informs [x0000000795580000 0, 0 x0000000795a0dc88, 0 x0000000797600000) from space 5120 k, 0%, informs [x0000000798000000 x0000000797b00000 0, 0 x0000000797b00000, 0) to space 5120 k, 0%, informs [x0000000797600000 0, 0 x0000000797600000, 0 x0000000797b00000) / / ParOldGen old s total 87552 k, used 61440K [0x0000000740000000, 0x0000000745580000, 0x0000000795580000) object space 87552K, 70%, informs [x0000000740000000 0, 0 x0000000743c00010, 0 x0000000745580000) / / dimension Metaspace informs the 3387 k, capacity 4496 k, committed 4864K, reserved 1056768K class space used 376K, capacity 388K, committed 512K, reserved 1048576KCopy the code
Obviously, this 60M of memory is opened in the old age, for a single object larger than the size of the younger generation is generally directly put in the old age, but here 60M is clearly enough, I can be the Mac Pro run code
Parent delegation mechanism
The parent delegation mechanism is written here to emphasize this point again, too many interview questions
Let’s go back to the ClassLoader design again:
public abstract class ClassLoader{
private final ClassLoader parent;
protectedClass<? > loadClass(String name,booleanresolve){ ...... }}Copy the code
The ClassLoader object is designed with a parent loader that looks like a linked list. The next in the list points to the next one, the ClassLoader parent, one level up here
By default, class loaders are not loaded by themselves. Instead, they are loaded by their own parent loader
The JVM has three levels of classloaders, each of which has its own range of classes that can be loaded. Each level of classloader submits to the parent loader for loading. When it encounters a class that can be loaded, the unloaded class is loaded, and the loaded class returns the loaded class object to the next level
Take a look at the classLoader.loadClass () method code:
protectedClass<? > loadClass(String name,boolean resolve)
throws ClassNotFoundException
{
// First, check if the class has already been loadedClass<? > c = findLoadedClass(name);if (c == null) {
try {
if(parent ! =null) {
c = parent.loadClass(name, false);
} else{ c = findBootstrapClassOrNull(name); }}catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.c = findClass(name); }}return c;
}
Copy the code
This is called the parent delegate mechanism, and why is it called parent, because the system class loader has two class loaders on it
The Java core class library has access restrictions, and the class loader will report an error when it finds a load request for a class that is not allowed to be loaded. This is usually based on the package name. For example, if you create a String class, the package name is java.lang, the bootloader will report an error when processing the load request. This error mechanism is called sandbox security
A typical example of a sandbox security mechanism is 360 sandbox isolation. For example, the usb drive program only runs in the sandbox that 360 considers isolated, in order to protect the system outside the sandbox from possible virus contamination
The purpose of the parent delegation mechanism is to ensure security and prevent the core API from being tampered with
Which chunk of memory is in the stack
I think this is definitely a question that can make you confused
This actually involves a mechanism of the JVM: top-of-stack caching
Since operands are stored in memory, frequent memory read/write operations are performed, which inevitably affects the execution speed. To solve this problem, the designers of the Hotspot VIRTUAL machine have come up with a stack top cache technique, in which the top of the stack elements are cached in the CPU registers to reduce the number of reads and writes to memory and improve the efficiency of the execution engine
L1->L2->L3 -> L1-> L3 -> L1-> L3 -> L1-> L3 -> L1-> L3 -> L1-> L3 ->L2->L3 -> L1-> L3 -> L1->L2->L3 -> L1-> L3 -> L1->L2->L3 There are also optimizations for this area based on the Java lock upgrade mechanism. For example, the top stack frame of a thread that will run again soon may be preloaded into the CPU cache. This time it is estimated to be L3
TLAB
TLAB – Thread private cache
Heap memory is shared between threads, and multiple threads operating on the same memory address can cause concurrency problems, which the JVM handles by locking. For this scenario object creation, multiple threads have to allocate space in the heap memory at the same time to create objects, this is a resource conflict, and object creation is very frequent, if every lock that performance can also see ~ △ ~
So TLAB produced, the best way to solve the problem is to eliminate the problem, not is resource conflict ( ̄  ̄ _) that we each thread to have a private space in the heap memory not to go, this is only a small piece of memory space for a thread to use no resource conflicts with, not need not lock, this strategy is called: rapid allocation policy
The default TLAB size is 1% of Eden per thread, so the heap memory will not be able to support more threads. I guess the JVM must also have optimization, which is not so simple
- XX: UseTLAB:
Enable TLAB, which is enabled by default-XX:+TLABSize
-XX:TLABWasteTargetPercent=1
Ratio of TLAB to Eden-XX:TLABRefillWasteFraction
-XX:+PrintTLAB
If TLAB fails, the JVM locks the atomicity of data operations and allocates memory directly in Eden
For more details, see JVM Optimization – (5) – Thread-local cache TLAB pointer collisions, Eden allocation
Stack analysis, escape analysis
There is an interview question: are objects allocated on the heap? This is a test of whether you know the escape point on the stack
As JIT compiler technology advances, optimization techniques such as allocation on the stack and scalar replacement can undermine the idea that all objects are allocated to heap memory
After analyzing escape techniques, if an object does not have an escape method, it may be optimized to allocate memory directly on the stack, rather than to create memory areas in the heap
Alibaba’s own JVM TaobaoVM innovated a technology called GCIH, in which objects with a long life cycle can be moved from the heap to the heap, so that they do not need to be managed by GC, so as to reduce the GC frequency and improve the EFFICIENCY of GC
What is on-stack allocation? Simply put, if the object created in a method has not been declared for a period beyond that method, the new object can be allocated directly to the memory of the stack frame, instead of going to the heap, which also has TLAB and lock
As analyzed earlier, IBM considers 80% of objects to be ephemeral, meaning that when an object is new within a method, the completion is destroyed as the method completes execution. If all objects are created in the heap memory, TLAB optimization will be carried out in the first moment. When the TLAB is not fit enough, locks will be added to the heap memory, and CAS operation will be added to open up the memory space. The creation and destruction of a large number of objects will frequently cause YGC
If such a dying object makes space in the stack frame, according to the stack memory and stack top cache mechanism, the stack frame to be executed will be loaded into the CPU cache, and then after the method ends, the object will be destroyed with the stack frame data. The advantages are:
- CPU cache speed is really fast, 100 times faster than memory
- You can also save some time by reducing communication between CPU and memory, which has a speed limit and is slow in terms of CPU clock cycles
- This greatly reduces heap memory stress, which can effectively reduce the frequency of YGC, which is important for code optimization
Note that stack allocation optimizes the new objects in the method and not the others
A typical on-stack assignment example:
public void name1(a){
String name = new String("AA");
int length = name.length();
}
Copy the code
The optimization is name, with stack allocation technology, name does not need to be in the heap space
In JDK7, escape analysis is enabled by default
- XX: + DoEscapeAnalysis:
Open escape analysis- XX: PrintEscapeAnalysis:
Print escape analysis results
Escape analysis is determined at compile time, and while we can be sure that the final object escaped, the compiler is not, as long as we are not 100% sure, then the new object in the method cannot be allocated on the stack
So the key to analyzing escape is to see if the object in the new method is referenced externally
Here’s an example where escape on the stack doesn’t work. Know your enemy
public class Max {
public Dog dog ;
/** * Escape failed, the object returned by the method may be a global variable * but it is not 100% certain for the compiler that the object lifetime is only within the method * so even if the new Dog is actually returned, the new object is allocated on the heap memory *@return* /
public Dog dog1(a){
return dog == null ? new Dog() : dog;
}
/** * escape failed, and the object reference to new was passed directly to the global variable */
public void getDog(a){
this.dog = new Dog();
}
public Dog getVlue(a){
return new Dog();
}
/** * The dog2() method does not exist * but for the new object in getVlue(), this object is cross-stack frame * stack frames cannot be accessed. So the new object in the getVlue() method must be created in the heap
public void dog2(a){ Dog dog = getVlue(); }}Copy the code
Final conclusion: If you can use local variables in your development, don’t define them outside the method
* * * * * * * * * * * * * * * * * *
The technology for escape analysis has been around since 1999 and it wasn’t until JDK1.6 Hotspot that it started supporting the initial escape analysis, even though the technology is still immature and there is a lot of room for improvement. The immature reason is that the calculation cost of escape analysis is very high, and the performance advantage brought by escape analysis is not even guaranteed to be higher than the calculation cost. In practical applications, especially large applications, it is found that escape analysis may appear unstable state. This technology is not turned on by default until JDK7, when Java programs in service mode are supported
From the JVM’s point of view, the example works well in code optimization, and more because of the scalar replacement described below. There are still a lot of Dog objects in the heap even after the escape analysis is enabled, which is quite different from the theory
String stitching interview questions
Interviewers all like to ask strings for interview questions, let alone pen questions. When is it necessary to include strings, especially String stitching
String concatenation has four results:
Declared as a literal, it will be stored in the string constant pool
Constant concatenation results in a string constant pool, which is optimized at compile time
As long as one of them is a reference variable, the result is stored in heap memory, and the principle is StringBuilder
If the concatenation result uses intern(), the result is saved to the string constant pool
Dry say unintentional, use proxy to speak below
1. Literal
String name = "abc";
Copy the code
“Xx” in double quotation marks is a literal, such a string will be optimized to save in the constant pool, just to say, because when saying literal, many people do not know what it is ~ (~ ▽ ~)(~ ▽ ~) ~
Whenever a String is quoted as “xx”, even if it is a new String(” ABC “), the corresponding String object is created in the constant pool at compile time
The String constant pool holds a reference to a String object, not the object itself. That’s important
Take this line of code as an example of the memory structure at this point
String name = new String("abc");
Copy the code
"abc"
2. Literal concatenation strings
String name1 = "a"+"b"+"c"
String name2 = "abc";
System.out.println(name1==name2); // true
System.out.println(name1.equals(name2)); // true
Copy the code
“A “+”b”+”c” are all literal concatenated strings like this, stored in the constant pool, so name1 and name2 have the same reference address, they both point to the same location in the constant pool of strings
This concatenation is optimized directly at compile time into the final result, which is ABC at compile time
Take a look at the decompiled bytecode:
0 ldc #14 <abc>
2 astore_1
3 ldc #14 <abc>
5 astore_2
6 getstatic #3 <java/lang/System.out>
9 aload_1
10 aload_2
11 if_acmpne 18 (+7)
14 iconst_1
15 goto 19 (+4)
18 iconst_0
19 invokevirtual #15 <java/io/PrintStream.println>
22 getstatic #3 <java/lang/System.out>
25 aload_1
26 aload_2
27 invokevirtual #16 <java/lang/String.equals>
30 invokevirtual #15 <java/io/PrintStream.println>
33 return
Copy the code
See two < ABC >, add <> to identify the constant pool
3. Reference the concatenation string
String name1 = "a";
String name2 = "b";
String name3 = "ab";
String name4 = name1+name2;
String name5 = name1+"b";
System.out.println(name3==name4); // false
System.out.println(name3==name5); // false
Copy the code
String concatenation as long as one of the concatenations is of a reference type, the concatenation result is not stored in the constant pool. Instead, it is stored in the heap and treated as an object
Look at the decompiled bytecode
0 ldc #16 <a> 2 astore_1 3 ldc #17 <b> 5 astore_2 6 ldc #18 <ab> 8 astore_3 9 new #4 <java/lang/StringBuilder> 12 dup 13 invokespecial #5 <java/lang/StringBuilder.<init>> 16 aload_1 17 invokevirtual #7 <java/lang/StringBuilder.append> 20 aload_2 21 invokevirtual #7 <java/lang/StringBuilder.append> 24 invokevirtual #9 <java/lang/StringBuilder.toString> 27 astore 4 29 new #4 <java/lang/StringBuilder> 32 dup 33 invokespecial #5 <java/lang/StringBuilder.<init>> 36 aload_1 37 invokevirtual #7 <java/lang/StringBuilder.append> 40 ldc #17 <b> 42 invokevirtual #7 <java/lang/StringBuilder.append> 45 invokevirtual #9 <java/lang/StringBuilder.toString> 48 astore 5 ...... 84 returnCopy the code
See, the bytecode has two new StringBuilders, and the result is that StringBuilder. ToString creates a new String object
Appen method is used to add data to the String array instead of creating a String object in the middle
@Override
public AbstractStringBuilder append(char c) {
ensureCapacityInternal(count + 1);
value[count++] = c;
return this;
}
Copy the code
This is very important. The interview will be fine. · ∀ ·) Blue
4. Take one last look
We have the following common string concatenation problem:
String name1 = "abc";
String name2 = "edf";
String name3 = "abcedf";
String name4 = "abc"+"edf";
String name5 = name1+"edf";
String name6 = "abc"+name2;
String name7 = name1+name2;
String name8 = name6.intern();
System.out.println(name3==name4); // true
System.out.println(name3==name5); // false
System.out.println(name3==name6); // false
System.out.println(name3==name7); // false
System.out.println(name5==name6); // false
System.out.println(name5==name7); // false
System.out.println(name6==name7); // false
System.out.println(name3==name8); // true
Copy the code
Intern () stores the characters of the heap memory string object into the string constant pool, which returns the address of the string in the constant pool if the string is present
Classic interview question: There are several string objects
String name = new String("abc");
String name2 = new String("AA") + new String("BB");
Copy the code
Once again:String name = new String("abc");
, memory, object allocation
"abc"
Q: How many objects are created for each of the above two sentences
Few people are right about this
The key to this problem lies in two items: literals and bytecodes. These items are not a problem ✧(≖ plus-one item ≖✿)
Literal: “AA” any String declared in double quotes, whether written directly to the right of = or inside a new String, will be stored in the String constant pool at compile time. But that doesn’t mean that new objects won’t come out of the heap. One heap memory, one string bytecode
1. String name = new String(“abc”);
Object 1:
New String Objects in heap memoryObject 2:
A constant object in the LDC string constant pool
All bytecode, how can we guess is useless, everything is determined at compile time
0 new #13 <java/lang/String>
3 dup
4 ldc #6 <abc>
6 invokespecial #14 <java/lang/String.<init>>
9 astore_1
10 return
Copy the code
Line 0, line 4, two objects, LDC is the open constant pool instruction
2. String name2 = new String(“AA”) + new String(“BB”);
Object 1:
New StringBuilder objectsObject 2:
New String objectThree objects:
LDC string constant poolObject 4:
New String objectObject 5:
LDC string constant poolObject 6:
Stringbuilder.tostring () -> Returns a String object
0 new #4 <java/lang/StringBuilder>
3 dup
4 invokespecial #5 <java/lang/StringBuilder.<init>>
7 new #13 <java/lang/String>
10 dup
11 ldc #14 <AA>
13 invokespecial #15 <java/lang/String.<init>>
16 invokevirtual #7 <java/lang/StringBuilder.append>
19 new #13 <java/lang/String>
22 dup
23 ldc #16 <BB>
25 invokespecial #15 <java/lang/String.<init>>
28 invokevirtual #7 <java/lang/StringBuilder.append>
31 invokevirtual #8 <java/lang/StringBuilder.toString>
34 astore_1
35 return
Copy the code
Lines 0, 7, 11, 19, 23, 31 total six objects. ToString returns a String object that does not use double “AA” quotes to declare a String. So there will be no optimizations at compile time
* Who cares how many objects you have when writing code? (@ ̄ * * @)
But you even you write the code will generate several objects all don’t know, so how did you know what heap memory, how can know what is good code, what is the bad code, GC maxed out how you deal with it, inescapably, programming knowledge base are associated, here you don’t know, a lot of questions you can’t handle, only meng force 2 words
String.intern () the interview questions
The intern method synchronizes the String from the String to the String constant pool and returns the address of the String in the constant pool. If there is no string object in the string constant pool, write the address of the string object that called intern to the string constant pool
Interview Question 1:
String name1 = new String("abc");
name1.intern();
String name2 = "abc";
System.out.println(name1 == name2);//false
Copy the code
- Name1 points to the address of the object new out of the heap
- Name2 points to the address of this string in the string constant pool, which was already created in the new object because of the literal
"abc"
Interview Question 2:
String name1 = new String("ab") + new String("c");
name1.intern();
String name2 = "abc";
System.out.println(name1 == name2);//true
Copy the code
- Name1 refers to the address of the object new in the heap, because there is no literal declaration, there is no ABC in the constant pool
- Name1. Intern submits the address of name1 to the string constant pool
- Name2 refers to the address of the string in the constant pool. Name1 submits the address of the string in the constant pool by name1
Interview question 3:
String name1 = new String("ab") + new String("c");
String name2 = "abc";
name1.intern();
System.out.println(name1 == name2);//false
Copy the code
- Because name2 created string objects in the constant pool via literals before name1.intern, name1 can’t commit itself to the constant pool
Interview question 4:
String name1 = new String("ab") + new String("c");
String name2 = "abc";
String name3 = name1.intern();
System.out.println(name3 == name2);//true
Copy the code
I don’t have to say that, but the intern method returns the address of that string in the constant pool
Interview question 5:
String name1 = new String("ab") + new String("c");
String name2 = name1.intern();
System.out.println(name1 == "abc");//false
System.out.println(name2 == "abc");//true
Copy the code