Look at stacks and heaps from a memory perspective!
We introduce the basic concept of function, in the end we mention a system exception java.lang.StackOverflowError, stack overflow error, to understand this error, we need to understand the implementation mechanism of function call. This section discusses its rationale from a conceptual model.
We've talked about the fundamentals of program execution before: The CPU has a directive indicator that points to the next instruction to execute, either sequentially or with a jump (conditional jump or unconditional jump).
Basically, this is still set up, the program from the main function start sequence execution, function call can be seen as an unconditional jump, jump to the corresponding function of the instruction to start execution, hit the return statement or the end of the function, then perform an unconditional jump, skip the transfer caller, Executes the next instruction after calling the function.
But here are a few questions:
- How are parameters passed?
- How does the function know where to go? In If/else, for, the address of the jump is OK, but the function itself does not know who will be called, and may be called in many places, it does not know in advance where to return after the end of execution.
- How does the result of the function pass to the caller?
The solution is to use memory to hold the data, and function callers and functions to reach a consistent agreement or convention on how to store and use the data. This convention is similar in all kinds of computer systems, where the memory that holds the data has the same name, called the stack.
Stack is a piece of memory, but its use has a special convention, is generally advanced after the out, similar to a bucket, put data into the stack, we call it into the stack, the bottom of what we call the stack, the top of what we call the stack, the top of what we are called from the stack, usually called out of the stack. The stack is generally from the high address to the low address extension, in other words, the memory address of the bottom of the stack is the highest, the top of the stack is the smallest.
The computer system mainly uses the stack to hold the data needed in the function call procedure, including the parameters, the return address, and the local variables defined within the function are also placed in the stack. The computer system makes a pact about how the data is stored in the stack, and how the caller and the function work together. The return value is not the same, it may be placed in the stack, but it uses the stack and local variables are not exactly the same, some systems use a memory stored in the CPU return value, we can simply think that there is a dedicated return value memory. The relevant data of the main function is placed at the bottom of the stack, and each time a function is called, the data of the relevant function is put into the stack and the call ends.
The above description may be somewhat abstract, as we illustrate by an example.
A simple example
Let's start with a simple example, here's the code:
1 public class Sum {2 3 public static int sum (int a, int b) {4 int c = a + B; 5 return C; 6 } 7 8 public static void Main (string[] args) {9 int d = sum.sum (1, 2); System.out.println (d); }12 13}
This is a simple example, the main function calls the SUM function, calculates the sums of 1 and 2, and then outputs the computed results, conceptually, which is easy to understand, let's discuss it from the perspective of the stack.
When the program calls Sum.sum before the main function, the stack is probably the case:
The main storage is two variables, args and D. In the case where the program executes into the Sum.sum function, before it is ready to return, that is, line 5th, the stack is probably like this:
We explain that when the main function calls Sum.sum, the arguments 1 and 2 are first put into the stack, then the return address (that is, the address of the instruction to execute after the function is called) is put into the stack, and then jumps to the SUM function, within the SUM function, you need to allocate a space for the local variable C, The parameter variables A and B correspond directly to the data 1 and 2 of the stack, and the return value is saved to the dedicated return value memory before returning.
After calling return, the program jumps to the return address saved in the stack, which is the next instruction address of main, and the data associated with the SUM function goes out of the stack, and then goes back to the following:
The next instruction in main is to assign a value to the variable d based on the return value of the function, and the return value is obtained from the dedicated return value memory.
The basic principle of function execution, in simple terms, is this. But there are some points that need to be introduced, let's discuss it.
The life cycle of a variable
As we said in the first verse, defining a variable allocates a chunk of memory, but we don't specifically talk about when to allocate memory, where to allocate it, and when to release memory.
From the above description of the stack we can see that the parameters in the function and the variables defined within the function are allocated in the stack, which are allocated only when the function is called, and are freed after the call is finished. But this is mainly about basic data types, and then we'll talk about arrays and objects.
Arrays and objects
For arrays and object types, we have introduced, they all have two blocks of memory, a piece of the actual content, a piece of the actual content of the address, the actual content space is not allocated on the stack, but allocated in the heap (also part of the memory, subsequent article introduction), but the space to store the address is allocated on the stack.
Let's look at an example, here's the code:
public class ArrayMax {public static int max (int min, int[] arr) { int max = min; for (int a:arr) { if (A>max) { max = A; } } return max; } public static void Main (string[] args) { int[] arr = new int[]{2,3,4}; int ret = max (0, arr); SYSTEM.OUT.PRINTLN (ret);} }
This program is also very simple, the main function creates an array, and then calls the function Max to calculate the maximum value of the elements in 0 and the array, which is probably the case in the memory stack and heap before the program executes to the return statement of the MAX function:
For an array of arr, the address of the actual content is stored in the stack, and the stack space for the 0x1000 is released as the stack is allocated, but the heap space that holds the actual content is not affected.
However, it is not correct to say that the heap space is completely unaffected, in this case, when the main function is executed, the Java system automatically reclaims the space when the stack space does not have a variable pointing to it.
Recursive invocation
We then use the stack to understand the recursive function of the invocation process, the code is as follows:
public static long factorial (int n) { long result = 1; for (int i=1; i<=n; i++) { result*=i; } return result;}
When factorial is first called, N is 4, and before execution to N*factorial (n-1), or 4*factorial (3), the stack is probably:
Note that the return value memory does not have a value, and after calling factorial (3), the stack becomes:
The depth of the stack is increased, the return value of the memory is still empty, so, each recursive call, the depth of the stack is increased by one layer, each call will be assigned to the corresponding parameters and local variables, will also save the return address of the call, when the call to n equals 0, the case of the stack is:
At this point, finally there is a return value, we will factorial abbreviated to F. The return value of f (0) is 1,f (0) returned to F (1), F (1) executes 1*f (0), the result is 1, then returns to F (2), F (2) executes 2*f (1), the result is 2, then goes back to F (3), F (3) executes 3*f (2), the result is 6, and then returns to F (4), Execute 4*f (3), the result is 24.
The above is the recursive function of the execution process, although the function code only one copy, but in the process of execution, every call, there will be one time into the stack, generating a different parameters, local variables and return address.
Cost of a function call
We can see from the process of the function call that there is a cost to the call, and that each call needs to allocate additional stack space for storing parameters, local variables, and return addresses, and additional stack and stack operations are required.
In the case of recursive invocation, if the number of recursion is more, the cost is quite considerable, so if the program can be more easily changed to another way, you should consider other ways.
In addition, the stack of space is not infinite, generally normal call is no problem, but like the example described in the previous section, the stack space is too deep, the system will throw an error, java.lang.StackOverflowError, that is, stack overflow.
Summary
This section describes the basic principle of function calls, function calls mainly through the stack to store related data, the system of function callers and functions how to use the stack to make a contract, the return value of our simplification is considered to be through a dedicated return value memory storage, we mainly conceptually introduced its basic principles, ignoring some of the details.
In this section, we assume that the modifier for the function is public static, which is slightly different if it is not static, as the following article describes.
We talked about, in Java, that functions have to be placed in classes, and now we simplify the idea that classes are just containers for functions, but the class is not only a function in Java, it also carries a lot of concepts and ways of thinking, in the next few sections, let's explore the world of classes together.
Reference: http://www.cnblogs.com/swiftma/p/5468104.html
Thinking Logic of computer program-the basic principle of function call