Java's JVM introduction and java's value passing and reference passing

background

During the interview, I encountered a basic java question, but I was confused by the question. After I came back, I felt like I would summarize this question

Pass by value and pass by reference in Java

Here, the specific value transfer and reference transfer will be passed, and the basic knowledge will be popularized before

type of data

In the Java virtual machine, data types can be divided into two categories: basic types and reference types. The variable of the basic type holds the original value, that is, the value he represents is the value itself; the variable of the reference type holds the reference value. "Reference value" represents a reference to an object, not the object itself, and the object itself is stored at the address indicated by the reference value.

basic type byte, short, int, long, char, float, double, Boolean
reference type Classes, Interfaces and Arrays

With the data type, the management of program data by the JVM is standardized. Different data types have different storage forms and locations. If you want to know how the JVM stores various types of data, you must first understand the JVM. Memory division and the functions of each part.

The division and function of JVM memory

The Java language itself cannot manipulate memory. Everything in it is managed and controlled by the JVM. Therefore, the division of the Java memory area is also the area division of the JVM. Before talking about the memory division of the JVM, let's take a look at Java The execution process of the program is as follows:

img

JVM structure diagram.png

As can be seen from the above figure: After the Java code is compiled into bytecode by the compiler, the JVM opens up a memory space (also called the runtime data area), which is added to the runtime data area through the class loader to store the data needed during program execution. data and related information.

Members of the JVM:
  1. virtual machine stack
  2. heap
  3. program counter
  4. method area
  5. native method stack
  • Virtual machine stack
    The virtual machine stack is the memory model for Java method execution. Stack frames are stored in the stack, and each stack frame corresponds to a called method. process.

The stack is thread-private, that is, the stack between threads is isolated; when a thread in the program starts to execute a method, it will create a corresponding stack frame and push it into the stack (at the top of the stack). After the method ends, The stack frame is popped.

img

Java stack model and stack frame structure diagram.png

栈帧: It is a data structure used to support the method call and method execution of the virtual machine, and it is a stack element of the virtual machine stack in the data area when the virtual machine is running.
Each stack frame includes:
1. Local variable table: used to store local variables (non-static variables, function parameters) in the method. When the variable is a basic data type, the value is directly stored, and when the variable is a reference type, a reference to a specific object is stored.
2. Operand stack: The interpreted execution engine of the Java virtual machine is called a "stack-based execution engine", and the stack referred to refers to the operand stack.
3. A reference to the runtime constant pool: a reference to a constant that may be used when the stored program is executed.
4. Method return address: store the return address after the execution of the method is completed.
相关的异常:
StackOverflowError: The stack depth requested by the thread is greater than the depth allowed by the virtual machine.
OutOfMemoryError: If the virtual machine stack can be expanded dynamically, but cannot apply for enough memory when expanding.

  • Heap
    The heap is used to store the object itself and the array. There is only one heap in the JVM, so the heap is shared by all threads.
  • method area
  1. The method area is a memory logic area shared by all threads. There is only one method area in the JVM, which is used to store some thread-shareable content. It is thread-safe. When multiple threads access the same content in the method area at the same time, only There can be one thread loading the data, and other threads can only wait.
  2. The content that can be stored in the method area includes: the full path name of the class, the fully qualified name of the direct superclass of the class, the access modifier of the class, the type of the class (class or interface), and the sequence of the fully qualified name of the direct interface of the class Lists, constant pools (fields, method information, static variables, type references (class)), etc.
  • The native method stack
    is different from the Java virtual machine stack in that the Java virtual machine stack serves for the virtual machine to execute Java methods (that is, bytecodes), while the local method stack serves for the Native methods used by the virtual machine. There will also be StackOverflowError and OutOfMemoryError exceptions.
  • The program counter
    has a small memory space and is thread-private. The job of the bytecode interpreter is to select the next bytecode instruction that needs to be executed by changing the value of the counter. Basic functions such as branching, looping, jumping, exception handling, and thread recovery all need to be completed by the counter.
The location of java data types in the JVM
  • The storage location of local variables is
    a variable declared in a method, that is, the variable is a local variable. Whenever a program calls a method, the system will create a method stack for the method, and the variables declared in the method are placed in the method stack. When the method ends, the system will release the method stack, and the corresponding variables declared in the method will end with the destruction of the stack, which is why local variables can only be valid in the method.
    The variables declared in the method can be variables of basic type or variables of reference type.
    (1) When the declaration is a basic type of variable, its variable name and value (variable name and value are two concepts) are placed in the method stack.
    (2) When declaring a reference variable, the declared variable (the variable actually stores the memory address value in the method) is placed on the stack of the method, and the object pointed to by the variable is placed on the heap class in storage.
    As shown in the picture:

    img

    Local variables are represented on the stack.png

  • The storage location of global variables
    The variables declared in the class are member variables, also called global variables, which are placed on the heap (because global variables will not be destroyed with the end of a method execution).
    Similarly, the variables declared in the class can be variables of basic type or variables of reference type
    (1) When declaring a variable of basic type, its variable name and its value are placed in the heap memory
    (2) of reference type, its The declared variable still stores a memory address value that points to the referenced object. Reference variable names and corresponding objects are still stored in the corresponding heap

    img

    The address of per in the figure points to an area in the heap memory

public class Person{
    
    
   private int age;
   private String name;
   private int grade;
 //篇幅较长,省略setter getter方法
   static void run(){
    
    
      System.out.println("run...."); 
    };
 }

//调用
Person per=new Person();
  • The storage location of the static variable of the basic data type
    The method area is used to store some shared data, so the static variable name and value of the basic data type are stored in the runtime constant pool of the method area, and the static variable is loaded with the class loading and disappears with the class and disappear.
Talk about the relationship between heap and stack
  1. The stack is the unit of runtime, while the heap is the unit of storage.
  2. The stack solves the running problem of the program, that is, how the program executes, or how to process the data; the heap solves the problem of data storage, that is, how and where to put the data.
  3. In Java, a thread will have a thread stack corresponding to it, which is easy to understand, because different threads execute logic differently, so an independent thread stack is required. The heap is shared by all threads. Because the stack is the unit of operation, the information stored in it is related to the current thread (or program). Including local variables, program running status, method return value, etc.; while the heap is only responsible for storing object information.

The performance of java value references on the heap and stack

In the method there is Print p1 = new Print(0, 0);

img

The address of the actual object is stored in the variable p1, which is generally called a "reference", and the reference points to the actual object. We call the actual object the value of the reference; the assignment operator = actually points the reference to the address of the value would work, if we had p1 = new Print(3,3); this would be the case:

img

It should be noted that the object Print(0,0) in the heap has not changed, only the address pointed to by reference p1 has changed.

pass by value

When the method is called, the actual parameter passes its content copy into the method through the formal parameter. At this time, the content received by the formal parameter is a copy of the actual parameter value, so any operation on the formal parameter in the method is only is an operation on this copy, without affecting the contents of the original value.

  • Take a chestnut:
public static void valueCrossTest(int age,float weight){
    
    
    System.out.println("传入的age:"+age);
    System.out.println("传入的weight:"+weight);
    age=33;
    weight=89.5f;
    System.out.println("方法内重新赋值后的age:"+age);
    System.out.println("方法内重新赋值后的weight:"+weight);
 }

public static void main(String[] args) {
    
    
      int a=25;
      float w=77.5f;
      valueCrossTest(a,w);
      System.out.println("方法执行后的age:"+a);
      System.out.println("方法执行后的weight:"+w);
}

Output:
Age passed in: 25
Weight passed in: 77.5
Age after reassignment in the method: 33
Weight after reassignment in the method: 89.5
Age after method execution: 25
Weight after method execution: 77.5
Output from above The results can be seen: after a and w are passed into valueCrossTest as actual parameters, no matter what operations are done in the method, a and w will not change in the end.

  • Conclusion:
    The change of age and weight only changes the content in the current stack frame (the stack frame where the valueCrossTest method is located). When the method execution ends, these local variables will be destroyed, and the stack frame where the mian method is located returns to the top of the stack, becoming When the current stack frame outputs a and w again, it is still the content at the time of initialization.
    Therefore:
    value transfer transfers a copy of the real content, and the operation on the copy does not affect the original content, that is, how the formal parameter changes will not affect the content corresponding to the actual parameter.
pass by reference

When the method is called, what is passed into the method is a copy of the actual parameter reference, so any operation on the formal parameter will not affect the actual parameter.

  • give a chestnut
public class Print {
    
    
    private int x;
    private int y;

    public Print(int x, int y) {
    
    
        this.x = x;
        this.y = y;
    }

    public void setLocation(int x, int y) {
    
    
        this.x = x;
        this.y = y;
    }

    private static void modifyPrint(Print p1, Print p2) {
    
    
        Print tmpPrint = p1;
        p1 = p2;
        p2 = tmpPrint;
        p1.setLocation(5, 5);
        p2 = new Print(5, 5);
    }

    public static void main(String[] args) {
    
    
        Print p1 = new Print(0, 0);
        Print p2 = new Print(0, 0);
        modifyPrint(p1, p2);
        System.out.println("[" + p1.x + "," + p1.y + "],[" + p2.x + "," + p2.y + "]");
    }

}

Running result: [0,0],[5,5]Is this result very unexpected, not the imagined [5,5], [5,5]
in the modifyPrint() method the following code

Point tmpPoint = p1;
 p1 = p2;
 p2 = tmpPoint;

It can be understood that there are formal parameters p1'=actual parameter p1 and formal parameters p2'=actual parameter p2 inside the method, so that the p1' and p2' we operate in the method are actually just temporary variables, and their life cycle is limited to the method.

img

Then call p1'.setLocation(5, 5); If the instance object itself provides a method to change itself, then the actual parameter will also be changed after the formal parameter calls the method, because they all point to the same instance, so this When the actual parameter p2 also becomes Point[5.5].
The code goes to the next line p2' = new Point(5, 5); Based on the discussion of the entire article, we can skip this line directly, because the reassignment operation of the formal parameter will not affect the actual parameter. The final stack information is as follows:

img

So the final answer is obvious: [0,0],[5,5]

 public static void main(String[] args) {
    
    
        List<String> colorList = new ArrayList<>();
        colorList.add("BLUE");
        colorList.add("RED");
        colorList.add("GRAY");
        System.out.println(colorList);
        removeFirst(colorList);
        System.out.println(colorList);
    }

    private static void removeFirst(List colorList) {
    
    
        if (!colorList.isEmpty()) colorList.remove(0);
    }

// 输出的结果
// [BLUE, RED, GRAY]
// [RED, GRAY]

In the above code, the removeFirst method is called, and the incoming parameter List is passed. The method internally calls the instance object itself, which provides a method to change itself, remove(int index), then the actual parameter will also be changed after the method is called with the formal parameter, because They all point to the same instance List, so the content of colorlist becomes [RED, GRAY], and the performance on the stack and heap is as follows:

img

If you adjust the above code: Modify the method content of removeFirst to the following code:

 private static void removeFirst(List colorList) {
    
    
        //if (!colorList.isEmpty()) colorList.remove(0);
        if (!colorList.isEmpty())
            colorList = colorList.subList(1, colorList.size());
    }
    
    //再次运行的结果为:
    //[BLUE, RED, GRAY]
    //[BLUE, RED, GRAY]

colorList.subList(1, colorList.size()); Although it is also a method provided by the instance object itself, this method returns a new new object. It is equivalent to colorList = new ArrayList(); It is equivalent to creating a new object in the method. The object declaration cycle of the formal parameter colorList' is only valid in removeFirst. When the method ends, the formal parameter colorList' is recycled, and the original actual parameter is still constant.

img

The source code of the subList method in the ArrayList class: SubList also implements the List interface

    public List<E> subList(int fromIndex, int toIndex) {
    
    
        subListRangeCheck(fromIndex, toIndex, size);
        return new SubList(this, 0, fromIndex, toIndex);
    }

Therefore: Call by value and Call by reference describe the evaluation strategy (Evaluation strategy) of parameters when a function is called, and describe the way of evaluating and taking values ​​when calling a function, while non-delivery content.

references

https://my.oschina.net/u/1455908/blog/392009
https://www.cnblogs.com/lfxiao/p/10599546.html
https://blog.csdn.net/bntx2jsqfehy7/article/details/83508006
https://blog.csdn.net/hongzhen91/article/details/90666066
https://www.cnblogs.com/zwbg/p/6194470.html

Guess you like

Origin blog.csdn.net/qq_43842093/article/details/131142866