递归太深导致栈溢出

栈溢出

在进行函数调用时,会将函数局部变量等信息压入栈中,在递归调用时如果层数太深,那么线程栈空间被耗尽,没有足够的内存分配给新创建的栈帧,就会导致java.lang.StackOverflowError

解决方案:

  1. 把递归调用函数更改为while或者for循环实现
  2. 通过尾递归优化,Java编译器不支持尾递归优化,可以手动实现但是此种实现方式不如使用循环进行优化直观
  3. 改用堆内存,函数中定义很大的局部变量,这种情况下可以将局部变量更改尾静态变量
  4. 增大栈的大小值,JVM中使用xss参数指定

实际案例

通过上面的解决方案可以看出,栈溢出通常有两种解决方法,第一是增大栈内存,第二是将递归更改为循环的方式。
增大栈内存大小虽然最为方便,但是如果递归的层数太深,不可能无限制的增大栈内存大小,仍旧会有栈溢出的情况出现,那么我们只有优化代码结构,将递归调用更改为循环调用。

树形结构,如果要遍历整个树进行一些统计,那么使用递归的方式最为方便直观,如下代码使用递归统计node节点下所有子节点的id

private void addChildren(List<String> ids, TreeNode node, ChildrenQuery query) {
		if (CollectionUtils.isEmpty(node.getChildren())) {
			return;
		}
		for (TreeNode child : node.getChildren()) {
			if (query.isCascaded()) {
                // 此处递归调用
				addChildren(ids, child, query);
			}
			ids.add(child.getId());
		}
	}

树十几万节点,如果使用递归的方式统计会导致java.lang.StackOverflowError,那么只能将递归更改为循环的方式,如下所示

private void addChildren(List<String> ids, TreeNode node, ChildrenQuery query) {

    if (CollectionUtils.isEmpty(node.getChildren())) {
        return;
    }

    Deque<TreeNode> stack = new ArrayDeque<>();
    node.getChildren().forEach(stack::push);

    while (!stack.isEmpty()) {

        TreeNode currentNode = stack.pop();

        if (CollectionUtils.isNotEmpty(currentNode.getChildren()) && query.isCascaded()) {
            for (TreeNode child : currentNode.getChildren()) {
                stack.push(child);
            }
        }
        ids.add(currentNode.getId());
    }
}
posted @ 2023-09-13 13:43  ~鲨鱼辣椒~  阅读(581)  评论(0)    收藏  举报