Java Switch语句-JVM内存分配、堆栈溢出
我在学校上Java课。我们的任务是设计一个类作为菜单,有几个子菜单 结构有点像这样(下面是一种伪代码,只是为了显示结构): 我的问题是:我的老师说这是错误的,因为如果我这样做,JVM会在内存中存储程序从一个地方到另一个地方的路径,从长远来看,这会导致堆栈溢出。他没有解释清楚,他只是说我应该用一个使用布尔变量的while循环来围绕整个过程,添加一个选项来翻转该布尔值以退出while循环,因为这样Java就不会存储程序从一个方法到另一个方法的路径 再一次,他没有详细解释,而且他解释的方式听起来非常混乱(根据他给我的信息,我试图尽可能清楚地解释)。在过去的3个小时里,我一直在网上寻找任何与他告诉我的相似的东西,但我找不到任何东西……所以我决定请教专家 你们能帮帮我吗?是的,你们的老师(部分)是对的。 关键的部分是,您可能从Java Switch语句-JVM内存分配、堆栈溢出,java,Java,我在学校上Java课。我们的任务是设计一个类作为菜单,有几个子菜单 结构有点像这样(下面是一种伪代码,只是为了显示结构): 我的问题是:我的老师说这是错误的,因为如果我这样做,JVM会在内存中存储程序从一个地方到另一个地方的路径,从长远来看,这会导致堆栈溢出。他没有解释清楚,他只是说我应该用一个使用布尔变量的while循环来围绕整个过程,添加一个选项来翻转该布尔值以退出while循环,因为这样Java就不会存储程序从一个方法到另一个方法的路径 再一次,他没有详细解释,而且他解释的方式听起来非常混
submenu1()
中调用mainMenu()
,并从submenu1()中调用submenu1()
。
如果每次在子菜单()中调用mainMenu()
,并在mainMenu()中调用子菜单()
,则程序将崩溃
对于每个函数调用,底层系统需要为函数的局部变量等保留内存。这就是所谓的stackframe。当您从函数本身(直接或间接)调用函数时,会调用它。递归需要在某个点返回。如果没有,您会因为内存不足而出现堆栈溢出。是的,您的老师(部分)是正确的。
关键的部分是,您可能从submenu1()
中调用mainMenu()
,并从submenu1()中调用submenu1()
。
如果每次在子菜单()中调用mainMenu()
,并在mainMenu()中调用子菜单()
,则程序将崩溃
对于每个函数调用,底层系统需要为函数的局部变量等保留内存。这就是所谓的stackframe。当您从函数本身(直接或间接)调用函数时,会调用它。递归需要在某个点返回。如果没有,则由于内存耗尽而导致堆栈溢出。当计算机执行方法/函数调用时,它必须:
记住调用函数正在做什么——局部变量的值,以及在被调用函数完成时在何处恢复执行李>
将控制转移到被调用函数
被调用函数完成后,它将:
将控制返回到调用函数中记忆的位置;好了,它
恢复局部变量的值等。;及
继续处理从被调用函数返回的值(如果有)
您的函数系统存在的问题是,它们可以一直调用,而且永远不会返回:
主菜单->子菜单1->主菜单->子菜单1。。。等等等等
如果您的函数从未返回,那么每次您进行新调用时,它只需不断记住越来越多的内容,可能在某个点超过可用于存储这些内容的(堆栈)内存量,从而导致堆栈溢出错误
有些语言实现了一种称为“tail call optimization”的优化,当调用另一个函数是您的函数所能做的最后一件事时,这种优化实际上可以避免存储这些内容。在这种情况下,它不再需要局部变量的值,也不需要记住在哪里恢复,因为它可以在调用函数中恢复,而调用函数已经被记住了
在这样的语言中,您的代码实际上可以正常运行。。。但是java不是这些语言之一。当计算机执行方法/函数调用时,它必须:
记住调用函数正在做什么——局部变量的值,以及在被调用函数完成时在何处恢复执行李>
将控制转移到被调用函数
被调用函数完成后,它将:
将控制返回到调用函数中记忆的位置;好了,它
恢复局部变量的值等。;及
继续处理从被调用函数返回的值(如果有)
您的函数系统存在的问题是,它们可以一直调用,而且永远不会返回:
主菜单->子菜单1->主菜单->子菜单1。。。等等等等
如果您的函数从未返回,那么每次您进行新调用时,它只需不断记住越来越多的内容,可能在某个点超过可用于存储这些内容的(堆栈)内存量,从而导致堆栈溢出错误
有些语言实现了一种称为“tail call optimization”的优化,当调用另一个函数是您的函数所能做的最后一件事时,这种优化实际上可以避免存储这些内容。在这种情况下,它不再需要局部变量的值,也不需要记住在哪里恢复,因为它可以在调用函数中恢复,而调用函数已经被记住了
在这样的语言中,您的代码实际上可以正常运行。。。但是java不是这些语言中的一种。请尝试使您的示例更符合java语法。目前,它看起来不像方法调用,但这似乎就是您的问题所在。那样的话,你的老师是对的。回想一下,方法调用是可以返回的,以恢复调用方法中的执行。当然,这是一种能力
public static void mainMenu() {
switch(integer variable){
case 1: submenu1();
break;
case 2: submenu2();
break;
}
}
public static void submenu1() {
switch(integer variable){
case 1: subsubmenu1();
break;
case 2: subsubmenu2();
break;
default: mainMenu(
}
}
public static void subsubmenu1() {
switch(integer variable) {
case 1: anothersubmenu1()
break;
case 2: anothersubmenu2();
break;
default: submenu1();
}
}