举例分析Java中的堆内存与栈内存

在运行Java程序时,有可能会遇到程序内存溢出而报错的情况,如下

package 面向对象设计模式;
import 面向对象设计模式.Person;
public class Test {
    
    

	public static void main(String[] args) {
    
    
		// TODO Auto-generated method stub
		Person p = new Person();
		p.eat();
	}

}
package 面向对象设计模式;

public class Person {
    
    
	public String name;
	public int age;
	public Person() {
    
    
		System.out.println("I......");
	}
	public void eat() {
    
    
		this.sleep();
		System.out.println("This is A....");
	}
	public void sleep() {
    
    
		this.eat();
		System.out.println("This is B...");
	}
}

在这里插入图片描述

这是由于栈内存溢出而导致的。

首先介绍在Java中,内存分为两种:

  • 堆内存:用于存储Java中的对象和数组,其特点是先进先出,后进后出(同时有入口和出口)。它是在运行时动态分配内存的,但也因此使得存取速度较慢。
  • 栈内存:主要用来存放方法或者局部变量等,用于执行程序用的。比如:基本类型的变量和对象的引用变量。它的特点是:先进后出,后进先出(只有一个口)。它的存取速度比堆要快,仅次于寄存器,栈数据可以共享,但也因此缺乏灵活性。

接下来分析例子中为何会使得栈内存溢出

  • Test类中的p对象存放在堆中,首先执行public Person(),然后调用Person类中的eat()方法。
  • 而Person类中的变量及方法都存储于栈中,当程序运行到eat()方法时,会在栈中创建一个void类的方法对象引用eat()方法(入栈),然后查找栈中有没有存放
{
    
    
		this.sleep();
		System.out.println("This is A....");
	}

这个方法,如果没有,则将方法存放进栈中,并令eat()指向其内容,如果已经有该方法内容,则直接令eat()指向其内容,相当于指向存放方法内容的地址。
在运行到eat()方法时由于遇到this关键字,调用了Person类中的其他方法sleep(),因此会在栈中创建一个void类的方法对象引用sleep()方法(入栈),然后查找栈中有没有存放

{
    
    
		this.eat();
		System.out.println("This is B...");
	}

这个方法,如果没有,则将方法存放进栈中,并令sleep()指向其内容,如果已经有该方法内容,则直接令sleep()指向其内容,相当于指向存放方法内容的地址。
而在运行sleep()方法时也遇到了this关键字,调用了Person类中的其他方法eat(),因此不断在栈中创建eat()和sleep()方法,且两个方法由于都无法结束,因此无法出栈,使得栈溢出,程序报错。

猜你喜欢

转载自blog.csdn.net/m0_55887872/article/details/124305246