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=1Ratio 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:

  1. CPU cache speed is really fast, 100 times faster than memory
  2. 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
  3. 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 memory
  • Object 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 objects
  • Object 2:New String object
  • Three objects:LDC string constant pool
  • Object 4:New String object
  • Object 5:LDC string constant pool
  • Object 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