You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决Java中Player实例递归通信高停止条件(10000次迭代)引发的StackOverflowError

解决递归引发的StackOverflowError问题

嘿,你的问题其实很典型:递归调用的深度超过了JVM调用栈的承载上限。每次调用sendMessage时,JVM都会在调用栈里新建一个栈帧来保存局部变量、返回地址等信息,当递归次数达到10000次时,栈空间直接被耗尽,就触发了StackOverflowError。Java默认的栈深度一般只有几千(具体取决于JVM配置),所以10、100次没问题,但10000次远远超标了。

根本解决方案:用迭代替代递归

递归虽然写起来简洁,但深度太大时必然会碰栈容量的天花板。换成循环迭代的方式就不一样了——所有逻辑都在同一个栈帧里执行,不会持续消耗栈空间,随便跑几万次都没问题。

下面是修改后的完整代码,我顺便优化了一些不合理的设计:

修改后的Player类

package play;
import java.util.ArrayList;
import java.util.List;

public class Player {
    private int id;
    private String playerName;
    private List<String> sentMessagesList = new ArrayList<String>();

    // Getters & Setters
    public List<String> getSentMessagesList() {
        return sentMessagesList;
    }
    public void setSentMessagesList(List<String> sentMessagesList) {
        this.sentMessagesList = sentMessagesList;
    }
    public int getID() {
        return id;
    }
    public void setID(int iD) {
        id = iD;
    }
    public String getPlayerName() {
        return playerName;
    }
    public void setPlayerName(String playerName) {
        this.playerName = playerName;
    }

    // ToString & Constructor
    @Override
    public String toString() {
        return "Player [ID=" + id + ", playerName=" + playerName + "]";
    }
    public Player(int iD, String playerName) {
        super();
        id = iD;
        this.playerName = playerName;
    }

    // 重写后的消息发送方法,用循环替代递归
    protected void sendMessage(String initialMessage, Player receiver) {
        Player currentSender = this;
        Player currentReceiver = receiver;
        String message = initialMessage;
        final int MAX_ITERATIONS = 10000;
        int iteration = 0;
        // 用StringBuilder优化字符串拼接,避免大量临时对象
        StringBuilder concatenatedCounts = new StringBuilder();

        while (iteration < MAX_ITERATIONS) {
            // 当前发送者记录已发送的消息
            currentSender.getSentMessagesList().add(message);
            // 获取当前发送者的已发送消息数(刚添加了一条,所以size就是最新计数)
            int sentCount = currentSender.getSentMessagesList().size();
            // 拼接计数
            concatenatedCounts.append(sentCount);
            // 输出消息
            System.out.printf("%s: %s%s%n", currentSender.getPlayerName(), message, concatenatedCounts);
            
            // 交换发送者和接收者,模拟互相回复的逻辑
            Player temp = currentSender;
            currentSender = currentReceiver;
            currentReceiver = temp;
            
            iteration++;
        }
    }
}

PlayerInteractions类(无需大改)

package play;

public class PlayerInteractions {
    public static void main(String[] args) {
        Player initiator = new Player(1, "Player1");
        Player receiver = new Player(2, "Player2");
        initiator.sendMessage("hello", receiver);
    }
}

关键改动说明:

  • 替换递归为循环:用while循环控制交互次数,彻底避免栈帧堆积。
  • 优化字符串拼接:用StringBuilder代替+=,10000次迭代下性能提升非常明显,不会产生大量无用的临时字符串。
  • 移除全局静态变量:原来的PlayerInteractions.concatenatedCounts是全局变量,不仅线程不安全,还容易导致意外的状态污染,改成局部的StringBuilder更合理。
  • 修正计数逻辑:原来的size()-1是错误的——刚把消息加入列表,size()就是当前已发送的消息数,不需要减1。

额外小提示:

虽然可以通过JVM参数(比如-Xss2m)调大栈容量,但这只是临时 workaround,迭代才是根本解决办法——毕竟栈空间再大也是有限的,迭代可以支持几乎无限的次数(只要堆内存足够)。

内容的提问来源于stack exchange,提问作者منى

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 03:47:32