1. Vm stack

1.1 Overview of the VM Stack

1.1.1 Background of the Virtual Machine Stack

Due to its cross-platform design, Java’s instructions are designed on a stack basis. Different platforms have different CPU architectures, so they cannot be register-based. Advantage is cross-platform, small instruction set; The downside is that performance degrades and more instructions are required to achieve the same functionality. The compiler is easy to implement.

1.2.2 Stacks and heaps in memory

Stack time is the unit of runtime, and heap is the unit of storage. The stack takes care of the execution of the program, that is, how the program executes, or how it processes data. The heap solves the problem of data storage, where and where data is stored.

1.2.3 Basic Contents of the VM Stack

  • What is a Java virtual machine stack

    • Java Virtual Machine Stack (Java Virtual Machine Stack), also known as the Java Stack. Each thread creates a virtual Stack with Stack frames that correspond to each Java method call
    • Is thread private
  • The life cycle

    • Lifecycle and thread consistency
  • role

    • Manages the execution of Java programs that hold local variables of methods (8 basic data types, reference addresses of objects), partial results, and participate in method calls and returns
  • The advantages of the stack

    • The stack is a fast and efficient way to allocate storage, second only to program counters in access speed
    • There are only two direct JVM operations on the Java stack:
      • Each method executes with a push (push, push)
      • Exit the stack after execution
    • There is no garbage collection problem for the stack
      • For program counters: no GC, no OOM
      • For stacks: no GC, OOM exists
  • Possible exceptions in the stack

    The Java Virtual Machine specification allows the size of the Java stack to be dynamic or fixed

    • With a fixed size Java virtual stack, the size of the Java virtual stack for each thread can be selected independently at thread creation time. The Java virtual machine will throw a StackOverflowError if the thread request allocates more stack capacity than the maximum allowed by the Java virtual machine stack
    • The Java virtual machine will throw an OutOfMemoryError if the Java virtual machine stack can be dynamically extended and cannot claim enough memory when attempting to extend it, or if there is not enough memory to create the corresponding virtual machine stack when a new thread is created
  • Set stack memory size

    • We can use the -xSS option to set the maximum stack space for a thread. The stack size directly determines the maximum reachable depth of a function call

      Reference: docs.oracle.com/en/java/jav…

    • Code demo

      By default, I is 10823 when StackOverflowError is thrown

      Set the -xss parameter and retry:

      I is 1873 when StackOverflowError is thrown

1.2 Storage unit of the stack

1.2.1 What is stored in the stack

  • Each thread has its own Stack, and the data in the Stack is stored in the format of Stack frames
  • Each method executing on this thread has a corresponding stack frame
  • A stack frame is a block of memory, a block of data that holds various data information during the execution of a method

1.2.2 Stack operation Principle

  • The JVM operates directly on the Java stack only two times, namely, pushing and unloading the stack frame, following the fifo/LIFO/FIFO principle
  • In an active thread, there is only one active stack frame at a time. That is, only the stack Frame (top stack Frame) of the currently executing Method is valid. This stack Frame is called the Current Frame. The corresponding Method to the Current Method is the Current Class.
  • All bytecode instructions run by the execution engine operate only on the current stack frame
  • If another method is called in this method, a new stack frame is created and placed at the top of the stack as the new current frame

  • The stack frames contained in different threads are not allowed to reference each other, that is, it is impossible to reference another thread’s stack frame in one stack frame
  • If the current method calls another method, when the method returns, the current stack frame will return the execution result of this method to the previous stack frame, and then the virtual machine will discard the current stack frame, making the previous stack frame become the current stack frame again
  • Java methods have two ways of returning functions. One is to return a normal function using a return directive. The other is to throw an exception (that is, not catch an exception). Either way, the stack frame will be ejected

1.2.3 Internal structure of stack frames

Each stack frame stores:

  • Local Variables (mainly affecting stack frame size)
  • Operand Stack (or expression Stack)
  • Dynamic Linking (or method reference pointing to the runtime constant pool)
  • Method Return Address (or definition of method normal exit or abnormal exit)
  • Some additional information

The dynamic link, method return address, and additional information are sometimes referred to as the frame data area.

1.3 Local Variables

1.3.1 overview

  • A local variable list is also called a local variable array or a local variable list

  • It is defined as a numeric array, mainly used to store method parameters and local variables defined in the method body. These data types include various basic data types (8 types), object reference, and returnAddress types

  • Since the local variable table is built on the thread stack and is thread private data, there is no data security problem

  • The size required by the local variables table is determined at compile time and stored in the Maximum Local Variables data item in the code attribute of the method. Local variable scale size does not change during method execution

  • The number of nested calls to a method is determined by the stack size. In general, the larger the stack, the more nested method calls. For a function, the more parameters and local variables it has, which causes the local variable table to swell, the larger its stack frame will be, to meet the need for more information to be passed through method calls. In turn, function calls take up more stack space, resulting in fewer nested calls

  • Local variable table variables are only valid in the current method call. During method execution, the virtual machine passes the parameter values to the parameter variable list using the local variable table. When the method call ends, the local variable table is destroyed along with the method stack frame

    The test code

    package com.nasuf.jvm;
    
    import java.util.Date;
    
    public class LocalVariableTest {
        private int count = 0;
    
        public static void main(String[] args) {
            LocalVariableTest test = new LocalVariableTest();
            int num = 10;
            test.test1();
        }
    
        public void test1(a) {
            Date date = new Date();
            String name1 = "nasuf"; String info = test2(date, name1); System.out.println(date + name1); }}Copy the code

    The main method is decomcompiled using javap and the following output is displayed:

        public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=2, locals=3, args_size=1    Note: locals=3 indicates that the maximum capacity of the local variable table is 3
             0: new           #3                  // class com/nasuf/jvm/LocalVariableTest
             3: dup
             4: invokespecial #4                  // Method "<init>":()V
             7: astore_1
             8: bipush        10
            10: istore_2
            11: aload_1
            12: invokevirtual #5                  // Method test1:()V
            15: return
          LineNumberTable:
            line 9: 0
            line 10: 8
            line 11: 11
            line 12: 15
          LocalVariableTable:    // Author's note: Local variation scale
            Start  Length  Slot  Name   Signature
                0      16     0  args   [Ljava/lang/String;    // Note: L represents the reference type, and here represents the method parameter args
                8       8     1  test    Lcom/nasuf/jvm/LocalVariableTest;    // Test indicates the local variable test
               11       5     2   num   I    // I is an Int, num is a variable
    Copy the code

    You can also use jclasslib to view the table of local variables in the main method in bytecode:

    The following figure shows the mapping between the line numbers of bytecode instructions and the line numbers of Java code

    Combine the following bytecode information

  • The part of the stack frame that is most relevant for performance tuning is the local variable table. When a method executes, the virtual machine uses a local variable table to complete the method’s delivery

  • Variables in the local variable table are also important garbage collection root nodes, as long as objects referenced directly or indirectly in the local variable table are not collected

1.3.2 Understanding of Slot

  • Parameter values are always stored at index0 of the local variable table array and end at the index of the array length -1

  • Local variable scale. The most basic storage unit is slot.

  • In the local variable table, types up to 32 bits occupy only one slot (including the returnAddress type), and 64-bit types (long and double) occupy two slots

    • Byte,short, and char are converted to int before storage. Boolean is also converted to int, where 0 means false and non-0 means true

    • Long and double occupy both slots

  • The JVM assigns an access index to each Slot in the local variable table, which successfully accesses the value of the local variable specified in the local variable table

  • When an instance method is called, its method parameters and local variables defined inside the method body are copied to each Slot in the local variable table in sequence

  • If you need to access the value of a 64bit local variable in the local variable table, you only need to use the previous index. (e.g., accessing a long or double variable)

  • If the current frame is created by a constructor or instance method, the object reference to this will be placed in Slot with index 0, and the rest of the arguments will continue in the argument list order. For static methods, the this variable does not exist in the local variable table with the current method, so the this reference cannot be used

  • Slots are reusable. If a local variable goes out of scope, then new local variables declared after its scope are likely to reuse the slot of the expired local variable, thereby saving resources

1.3.3 Comparison of various variables

  • Java variable classification:

    • By data type:
      • Basic data types
      • Reference data type
    • According to the position declared in the class:
      • Member variables (which undergo default initialization assignments before use) :
        • Static variables (or class variables) are assigned default values during the prepare phase of linking. Explicit assignment of class variables in the Initial phase, i.e., static code block assignment)
        • Instance variables (as the object is created, instance variable space is allocated in the heap space and default assignment is made)
      • Local variables (must be explicitly assigned before use, otherwise compilation fails; There is no default system initialization assignment.

1.4 Operand Stack

  • Each independent Stack frame contains a last-in-first-out operand Stack, also known as the Expression Stack, In addition to the local variable table.
  • Operand stack, during the execution of a method, data is written to or extracted from the stack according to bytecode instructions, i.e. push/ pop
    • Some bytecode instructions value the sub – operand stack, and the rest take the operand off the stack. Use them and push the results onto the stack; Such as performing copy, swap, and sum operations

  • If the called method has a return value, the return value is pushed into the operand stack of the current stack frame and updates the NEXT bytecode instruction to be executed in the PC register
  • The data types of the elements in the operand stack must exactly match the sequence of bytecode instructions, which is verified by the compiler at compile time and again during the data flow analysis phase during the class validation phase during class loading
  • The Interpretation engine of the Java Virtual machine is a stack-based execution engine, where the stack refers to the operand stack
  • Operand stack, mainly used to store the intermediate results of the calculation process, and as a temporary storage space for variables during the calculation process
  • The operand stack is a workspace of the JVM execution engine. When a method is first executed, a new stack frame is created. The operand stack of this method is empty
  • Each operand stack has an explicit stack depth for storing values, and the maximum depth required is defined by the compiler (The data structure aspect of the operand stack is implemented using arrays), stored in the Code property of the method, is the value of max_stackThe output can also see the maximum depth of the operand stack after decompiling the bytecode using Javap:
  • Any element in the stack can be any Java data type
    • 32-bit types occupy one stack unit depth
    • 64-bit types occupy two stack units of depth
  • Instead of accessing an index, the operand stack can only access data once through standard push and push operations

1.4.1 Code tracing

public class OperandStackTest {

    public void testAddOperation(a) {
        // byte, short, Boolean, char are all stored as int
        byte i = 15;
        int j = 8;
        intk = i + j; }}Copy the code

Bytecode instruction parsing:

For method calls that have a return valueaload_0Instruction to push the return value of the called method directly onto the operand stack; Also, if the method has a return value (such as int), the corresponding bytecode instruction isireturn

1.5 Top-of-stack Cashing

  • The zero-address instructions used by stack-based virtual machines are more compact, but more loading and unloading instructions are required to complete an operation, which means more instruction dispatch times and memory reads and writes
  • Because operands are stored in memory, frequent memory reads and writes inevitably affect execution speed. In order to solve this problem, HotSpot JVM designers proposed the technology of stack top cache, which caches all the top elements of the stack in the physical CPU registers to reduce the number of memory reads and writes, and improve the performance of the execution engine

1.6 Dynamic Linking

  • Each stack frame contains an internal reference to the method that the stack frame belongs to in the runtime constant pool. The purpose of including this reference is to enable the code supporting the current method to implement dynamic linking, such as the InvokeDynamic directive
  • When a Java source file is compiled into a bytecode file, all variable and method references are kept as Symbolic references in the class file’s constant pool. For example, describing a method that calls another method is represented by symbolic references to the method in the constant pool, so dynamic linking is used to convert these symbolic references to direct references to the calling method
  • The constant pool in the bytecode file is put into the method area after the JVM runs and becomes the runtime constant pool

For example for the following code:

public class DynamicLinkingTest {

    int num = 10;

    public void methodA(a) {
        System.out.println("methodA() ...");
    }

    public void methodB(a) {
        System.out.println("methodB() ..."); methodA(); num ++; }}Copy the code

Part of the output after compiling bytecode using Javap:

Where #3, #6, etc. represent symbolic references to the constant pool. Constant pool is used to provide symbols and constants for instruction identification

1.7 Method invocation

1.7.1 overview

In the JVM, the conversion of a symbolic reference to a direct reference to the calling method is related to the method binding mechanism

  • Static linking. Static linking is the process of converting a symbolic reference to a direct reference to a called method when a bytecode file is loaded into the JVM if the target method being called is known at compile time and the runtime remains the same
  • Dynamic linking. If the invoked method cannot be determined at compile time, that is to say, the symbolic reference of the invoked method can only be converted into a direct reference during the program run time. Because this reference conversion process is dynamic, it is called dynamic linking

The corresponding method Binding mechanisms are: Early Binding and Late Binding. Binding is the process by which a symbolic reference to a field, method, or class is replaced with a direct reference, which happens only once

  • Early binding means that the target method being called can be bound to the type to which it belongs if the target method is known at compile time and the runtime remains unchanged. This way, because you know exactly which target method is being called, you can use static links to convert symbolic references to direct references
  • Late binding refers to that the target method to be called cannot be determined at compile time, and only the related method can be bound according to the actual type during the running of the program. This binding mode is called late binding

Code demo

package com.nasuf.jvm;

class Animal {
    public void eat(a) {
        System.out.println("Animal feeding."); }}interface Huntable {
    void hunt(a);
}

class Dog extends Animal implements Huntable {

    @Override
    public void eat(a) {
        System.out.println("Dogs eat bones.");
    }

    @Override
    public void hunt(a) {
        System.out.println("Dog feed"); }}class Cat extends Animal implements Huntable {

    public Cat(a) {
        super(a);// Early binding
    }

    public Cat(String name) {
        this(a);// Early binding
    }
    @Override
    public void eat(a) {
        super.eat();
        System.out.println("Cats eat fish.");
    }

    @Override
    public void hunt(a) {
        System.out.println("Cat prey"); }}public class AnimalTest {
    public void showAnimal(Animal animal) {
        animal.eat();   // Late binding
    }

    public void showHun(Huntable h) {
        h.hunt();   // Late binding}}Copy the code

View the bytecode of the AnimalTest class as follows:

The bytecode of the Cat class is as follows:

As a high-level language, similar to the Java based on object oriented programming language nowadays more and more, even though this type of programming language in grammar has certain difference on the style, but they always keep each other with a common, that is both support object-oriented features such as encapsulation, inheritance, and polymorphism, since this kind of programming languages have polymorphism characteristics, Naturally, there are two types of binding: early binding and late binding.

Virtually any ordinary method in Java has the characteristics of virtual functions (late bindings), which are the equivalent of virtual functions in C++ (which are explicitly defined using the keyword virtual). If you do not want a method to have the characteristics of a virtual function ina Java program, you can mark the method with the keyword final. (Methods cannot be overridden, are determined at compile time, and do not have polymorphic characteristics.)

1.7.2 Virtual methods and Non-virtual methods

If a method is called at compile time, that version is immutable at run time. Such methods are called non-virtual methods. Static methods, private methods, final methods, instance constructors, and superclass methods are all non-virtual methods; the rest are called virtual methods

The virtual machine provides the following method call instructions:

  • Ordinary call instructions:
    • Invokestatic: Invokes static methods, and the parsing phase determines the unique method version
    • Invokespecial: Call the

      method, private and parent methods, and determine the unique method version in the parsing phase
    • Invokevirtual: Calls all virtual methods
    • Invokeinterface: Invokes interface methods
  • Dynamic call instruction:
    • invokedynamic: Dynamically resolves the method to be called and executes it
      • The JVM bytecode instruction set was stable until Java7 added an InvokeDynamic instruction, an improvement Java made for dynamically typed language support
      • In Java7, however, there is no way to generate invokedynamic instructions directly. You need to use ASM, the underlying bytecode tool, to generate invokedynamic instructions. It wasn’t until the advent of Java8 Lambda expressions that invokedynamic instructions were generated directly in Java

      • The dynamic language type support added in Java7 is essentially a modification of the Java virtual machine specification, not a modification of the Java language rules. This is a relatively complex area, and the addition of method calls in the virtual machine will most directly benefit the dynamic language compiler running on the Java platform

The first four instructions are fixed inside the virtual machine, and the method invocation is performed without human intervention, whereas the InvokeDynamic instruction allows the user to determine the method version. The invokestatic and Invokespecial commands call non-virtual methods, and the rest (excluding final modifications) are virtual methods

The code and corresponding bytecode instructions are parsed as follows:

package com.nasuf.jvm;

class Father {
    public Father(a) {
        System.out.println("The constructor of father.");
    }

    public static void showStatic(String str) {
        System.out.println("father " + str);
    }

    public final void showFinal(a) {
        System.out.println("father show final");
    }

    public void showCommon(a) {
        System.out.println("Father's Common method"); }}public class Son extends Father {
    public Son(a) {
        // Invokespecial non-virtual method
        super(a); }public Son (int age) {
        // Invokespecial non-virtual method
        this(a); }/** * static methods cannot be overridden, even though the method signature is exactly the same as the parent method@param str
     */
    public static void showStatic(String str) {
        System.out.println("son " + str);
    }

    private void showPrivate(String str) {
        System.out.println("son private " + str);
    }

    public void show(a) {
        // Invokestatic non-virtual method
        showStatic("nasuf");

        // Invokestatic non-virtual method
        super.showStatic("good");

        // Invokespecial non-virtual method
        showPrivate("hello");

        // Invokespecial non-virtual method
        super.showCommon();

        // Invokevirtual because showFinal() is a final method of its parent class, it cannot be overridden by subclasses.
        // So this is still non-virtual
        showFinal();
        // If the showFinal() method of the parent class is explicitly referenced, the bytecode instruction becomes invokespecial
        // super.showFinal()

        // Invokevirtual because super.showcommon () is not explicitly specified, this method may be overridden by subclasses and cannot be determined at compile time
        // Therefore virtual method
        showCommon();

        // invokevirtual
        info();

        MethodInterface methodInterface = null;
        // InvokeInterface virtual method, which must be implemented by the implementation class at execution time
        methodInterface.methodA();
    }

    public void info(a) {}public void display(Father f) {
        f.showCommon();
    }

    public static void main(String[] args) {
        Son son =  newSon(); son.show(); }}interface MethodInterface {
    void methodA(a);
}
Copy the code

1.7.3 Dynamically typed and statically typed languages

The difference is whether the type is checked at compile time or re-run time. Statically typed languages are statically typed, whereas dynamically typed languages are. In other words, statically typed languages judge the type information of variables themselves; Dynamically typed language is the type information used to judge the value of a variable. The value of a variable has type information only when there is no type information, which is an important feature of dynamic language. Java is a statically typed language (some of the features that functional programming lambda expressions give Java to dynamic languages), and Javascript/Python is a dynamically typed language

1.7.4 Nature of method rewriting

  1. Find the actual type of the object executed by the first element at the top of the operand stack and call it C
  2. If a method is found in type C that matches both the descriptor and the simple name in the constant, access is checked. If it passes, a direct reference to the method is returned, and the search process ends. If not through, it returns the Java. Lang. IllegalAccessError anomalies
  3. Otherwise, step 2 search and verify each parent class of C from the bottom up according to the inheritance relationship
  4. If didn’t find the right way, it throws the Java. Lang. AbstractMethodError anomalies

IllegalAccessError: A program attempts to access or modify a property or call a method that does not have permission to access it. Normally this will cause a compiler exception. This error, if it occurs at run time, indicates an incompatible change to a class

1.7.5 Virtual Method table

  • In object-oriented programming, dynamic dispatch is frequently used, and it may affect the execution efficiency if the class’s method metadata has to be searched again for the appropriate target during each dynamic dispatch. Therefore, to improve performance, the JVM implements a virtual Method table in the method section of the class (non-virtual methods do not appear in the table). Use index tables instead of lookups
  • Each class has a virtual method table that holds the actual entry to each method
  • The virtual method table is created and initialized during the linking phase of the class load, and the JVM initializes the method table for that class after the class’s variable initializers are ready

Example 1:

Example 2:

Code:

package com.nasuf.jvm;

interface Friendly {
    void sayHello(a);
    void sayGoodbye(a);
}

class Dog {
    public void sayHello(a) {}public String toString(a) {
        return "Dog"; }}class Cat implements Friendly {

    public void eat(a) {}@Override
    public void sayHello(a) {}@Override
    public void sayGoodbye(a) {}protected void finalize(a) {}public String toString(a) {
        return "Cat"; }}class CockerSpaniel extends Dog implements Friendly {

    public void sayHello(a) {
        super.sayHello();
    }

    @Override
    public void sayGoodbye(a) {}}public class VirtualMethodTable {}Copy the code

Dog virtual method table

CockerSpaniel virtual method table

Cat Virtual method table

1.8 Method Return Address

  • Holds the value of the PC register that called the method (the PC register holds the value of the next instruction to be executed by the method)
  • There are two ways to end a method:
    • When the execution engine encounters a bytecode instruction (return) returned by any method, the return value is passed to the upper method callerNormal exit completion;
      • Which return instruction Sakai needs to use after a normal call to a method also depends on the actual data type of the method return value
      • In the bytecode instructions, return instructions contain ireturn (when the return value is a Boolean, byte, char, shor, int), lreturn, freturn, dreturn, areturn, There is also a return directive for methods declared as void, power initializers, and class and interface initializers
    • An Exception encountered during the execution of a method that is not handled within the method will cause the method to exit as long as no matching Exception handler is found in the Exception table of the method. Abnormal completion exit for short. Exception handling when an exception is thrown during the execution of a method is stored in an exception handling table, which is convenient to find the code to handle exceptions when exceptions occur
  • Either way, the method is returned to where it was called after it exits. When a method exits normally, the value of the caller’s PC counter is returned as the address of the next instruction that calls the method. However, if an exception exits, the return address is determined by the exception table, which is generally not stored in the stack frame
  • In essence, the method exit is the process of the current stack frame out of the stack. At this point, it is necessary to restore the local variable table of the upper method, operand stack, push the return value into the operand stack of the caller’s stack frame, set the PC register value, etc., and let the caller’s method continue to execute
  • The difference between a normal completion exit and an exception completion exit is that an exception completion exit does not return any value to its upper callers
package com.nasuf.jvm;

import java.io.FileReader;
import java.io.IOException;
import java.util.Date;

public class ReturnAddressTest {

    // The default constructor is return

    // ireturn
    public boolean methodBoolean(a) { return false; }

    // ireturn
    public byte methodByte(a) { return 0; }

    // ireturn
    public short methodShort(a) { return 0; }

    // ireturn
    public char methodChar(a) { return 'a'; }

    // ireturn
    public int methodInt(a) { return 0; }

    // lreturn
    public long methodLong(a) { return 0L; }

    // freturn
    public float methodFloat(a) { return 0.0 f; }

    // dreturn
    public double methodDouble(a) { return 0.0; }

    // areturn: reference type
    public String methodString(a) { return null; }

    // areturn: reference type
    public Date methodDate(a) { return null; }

    // return
    public void methodVoid(a) {}static {
        int i = 10;
    }

    public void method2(a) {
        try {
            method1();
        } catch(IOException e) { e.printStackTrace(); }}public void method1(a) throws IOException {
        FileReader fis = new FileReader("test.txt");
        char[] cBuffer = new char[1024];
        int len;
        while((len = fis.read(cBuffer)) ! = -1) {
            String str = new String(cBuffer, 0, len); System.out.println(str); } fis.close(); }}Copy the code

Execute javap with method2 and the following output is displayed (note Exception table) :

public void method2(a);
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=2, args_size=1
         0: aload_0
         1: invokevirtual #2                  // Method method1:()V
         4: goto          12
         7: astore_1
         8: aload_1
         9: invokevirtual #4                  // Method java/io/IOException.printStackTrace:()V
        12: return
      Exception table:
         from    to  target type
             0     4     7   Class java/io/IOException    
      LineNumberTable:
        line 50: 0
        line 53: 4
        line 51: 7
        line 52: 8
        line 54: 12
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            8       4     1     e   Ljava/io/IOException;
            0      13     0  this   Lcom/nasuf/jvm/ReturnAddressTest;
      StackMapTable: number_of_entries = 2
        frame_type = 71 /* same_locals_1_stack_item */
          stack = [ class java/io/IOException ]
        frame_type = 4 /* same */
Copy the code

Indicates that if the code is abnormal in the range from bytecode instruction 0 to 4 (line number 50 to 53), the byte code instruction 7 (line number 51) position is processed:

1.9 Additional information

Stack frames also allow you to carry additional information about the Java virtual machine implementation. For example, support information for program debugging

1.10 Interview questions: Are local variables defined in the method thread-safe?

Code parsing:

package com.nasuf.jvm;

/** * StringBuilder itself is not thread-safe */
public class StringBuilderTest {

    /** * s1 is declared inside a method, used only inside a method, and is thread-safe */
    public static void method1(a) {
        StringBuilder s1 = new StringBuilder();
        s1.append("a");
        s1.append("b");
        // ...
    }

    /** * s2 is passed outside the method and is not thread-safe */
    public static void method2(StringBuilder s2) {
        s2.append("a");
        s2.append("b");
    }

    /** * s3 is returned by methods and may be called by other threads, not thread-safe */
    public static StringBuilder method3(a) {
        StringBuilder s3 = new StringBuilder();
        s3.append("a");
        s3.append("b");
        return s3;
    }

    /** * S4 is thread-safe. The toString() method of StringBuilder returns a new String. The toString object itself is not thread-safe because it will be called by other threads@Override
     *     public String toString() {
     *         // Create a copy, don't share the array
     *         return new String(value, 0, count);
     *     }
     */
    public static String method4(a) {
        StringBuilder s4 = new StringBuilder();
        s4.append("a");
        s4.append("b");
        returns4.toString(); }}Copy the code

2. Local method stack

  • The Java virtual machine stack is used to manage the invocation of Java methods, and the local method stack is used to manage the invocation of local methods
  • The local method stack is also thread-private
  • Allows memory sizes to be implemented as fixed or dynamically expandable. It’s the same in terms of memory overflow
    • The Java virtual machine will throw a StackOverflowError if the thread request allocates more stack capacity than the maximum allowed by the local method stack
    • The Java virtual machine will throw an OutOfMemoryError if the local method stack can be extended dynamically and there is not enough memory to allocate when trying to extend it, or there is not enough memory to create the corresponding local method stack when a new thread is created
  • The native methods are implemented using the C language
  • It is implemented by registering Native methods in the Stack and loading the local Method library at Execution Engine time
  • When a thread calls a local method, it enters a whole new world that is no longer constrained by the virtual machine. It has the same rights as the VIRTUAL machine
    • Local methods access the runtime data area inside a virtual machine through a local interface
    • It can even use the registers in the local processor directly
    • Allocate any amount of memory directly from the heap of local memory
  • Not all JVMS support native methods. Because the Java virtual Machine specification does not specify the language, implementation, data structure, and so on of the native method stack. If the JVM product is not intended to support native methods, you may not need to implement them
  • In HotSpot JVM, the local method stack and virtual machine stack are combined directly