在深入Java编程的世界时,理解其方法调用背后的内存管理机制是至关重要的。
Java作为一种面向对象的语言,其内存管理自动化程度高,但背后涉及的原理却错综复杂,尤其是方法调用过程中的栈帧、堆、方法区等概念。
本文将通过代码示例、图文并茂地揭示Java方法调用的栈内存
原理,帮助你构建更稳固的Java基础。
引言
Java程序在执行时,其内存主要被划分为几个关键区域:堆(Heap)、栈(Stack)、方法区(Method Area)、程序计数器(Program Counter Register)以及本地方法栈(Native Method Stack)。其中,方法调用的内存管理主要涉及栈和方法区。
先用一个简单的例子快速了解下这三个区域:
package org.example;
public class Person {
private String name;
private int age;
public Person(){}
public Person(String name, int age){
this.name = name;
this.age = age;
}
}
public class JvmDemo1 {
public static void main(String[] args) {
int num = 10;
Person person = new Person("张三", 20);
Person person1 = new Person();
}
}
当运行main方法时,Jvm的内存分布如下图:
接下来,将通过实例来深入分析方法调用过程中,栈内存是如何变化的。
一,栈内存与方法调用
栈内存是线程私有的,用于存储方法调用时的局部变量、操作数栈以及方法出口等信息。
一个线程会创建一个虚拟机栈,不同线程的虚拟机栈不共享。
本文以单线程为例分析方法调用时的内存分布和变化。
1,针对方法调用,虚拟机做了什么
- ①每当一个方法被调用时,Java虚拟机(JVM)都会为该方法创建一个新的栈帧(Stack Frame),并将其压入当前线程的栈顶。
如下图:
- ①Jvm会为执行main方法的线程分配一块栈内存,称之为
虚拟机栈
- ②执行main方法时,会在虚拟机栈创建一个栈帧,并压入栈底
- ③main方法中的局部变量会存储在该栈帧中
- ②方法执行完毕后,相应的栈帧会被弹出,释放资源。如下图,main方法执行完毕,对应的栈帧被弹出,因为没有其他方法调用,所以栈内存没有其他栈帧。
这个过程形象地展示了函数调用的“先进后出”
(LIFO)原则。
2,方法链式调用时的内存分布
public class MethodDemo {
public static void main(String[] args) {
eat();
}
private static void eat() {
study();
System.out.println("吃饭");
sleep();
}
private static void study() {
System.out.println("学习");
}
private static void sleep() {
System.out.println("睡觉");
}
}
解析
-
当
main
方法开始执行时,为其分配一个栈帧
-
调用
eat
方法时,JVM为eat
方法创建新的栈帧
-
eat
方法调用study方法,JVM为study
方法创建新的栈帧
-
study
方法不再调用其他方法,所以JVM不再创建新的栈帧,study
方法执行完成后,对应的栈帧被移除
-
study栈帧被移除后,JVM继续执行
eat
栈帧对应的代码,先打印“吃饭”,后调用sleep
方法,JVM将为sleep
方法创建新的栈帧
-
sleep
方法执行完成后,栈帧被移除
-
sleep
栈帧被移除后,继续执行eat
栈帧后续的代码,eat
方法执行完成,对应栈帧被移除
-
eat
方法执行完成后,回到main
方法,main
方法没有其他需要执行的代码,其栈帧也会被移除