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

Bukkit插件:破坏方块写入SQLite触发StackOverflowError问题排查

Bukkit插件BlockBreakEvent触发StackOverflowError的原因及解决方案

错误原因

核心问题是循环实例化与事件监听递归注册:

  • 你的SqliteDB类构造函数中大概率存在实例化EventBlock(事件监听类)或重复注册BlockBreakEvent监听的逻辑。
  • 每次玩家破坏方块触发BlockBreakEvent时,你在事件处理方法里新建SqliteDB实例,而SqliteDB初始化又会触发EventBlock的实例化/事件注册,后续事件触发时又会重复这个流程,形成无限递归调用,最终耗尽栈内存抛出java.lang.StackOverflowError。
  • 每次事件都新建数据库连接,会快速消耗栈资源,进一步加剧溢出问题。

解决方案

1. 用单例模式复用SqliteDB实例

不要在事件方法内每次创建SqliteDB对象,而是在插件启动时初始化全局实例,事件中直接调用:

// 插件主类
public class YourPlugin extends JavaPlugin {
    private static SqliteDB dbInstance;

    @Override
    public void onEnable() {
        // 初始化数据库实例,仅执行一次
        dbInstance = new SqliteDB();
        // 注册事件监听
        getServer().getPluginManager().registerEvents(new EventBlock(), this);
    }

    // 提供全局访问方法
    public static SqliteDB getDbInstance() {
        return dbInstance;
    }

    @Override
    public void onDisable() {
        // 插件关闭时关闭数据库连接
        if (dbInstance != null) {
            dbInstance.close();
        }
    }
}

// 事件监听类
public class EventBlock implements Listener {
    @EventHandler
    public void onBlockBreak(BlockBreakEvent event) {
        Player player = event.getPlayer();
        Location loc = event.getBlock().getLocation();
        // 直接调用全局数据库实例执行插入
        YourPlugin.getDbInstance().insert(loc, player);
    }
}

2. 修复SqliteDB的循环依赖

检查SqliteDB的构造函数,移除所有实例化EventBlock或注册BlockBreakEvent的代码,彻底切断循环触发的源头。

3. 复用数据库连接

在SqliteDB内部维护持久化的数据库连接,避免每次插入都新建连接:

public class SqliteDB {
    private Connection conn;

    public SqliteDB() {
        try {
            // 初始化连接,仅执行一次
            conn = DriverManager.getConnection("jdbc:sqlite:plugins/YourPlugin/break_log.db");
            // 初始化表结构
            createTable();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    private void createTable() {
        String createSql = "CREATE TABLE IF NOT EXISTS break_log (" +
                "id INTEGER PRIMARY KEY AUTOINCREMENT," +
                "player_name TEXT NOT NULL," +
                "x INTEGER NOT NULL," +
                "y INTEGER NOT NULL," +
                "z INTEGER NOT NULL," +
                "world TEXT NOT NULL)";
        try (Statement stmt = conn.createStatement()) {
            stmt.execute(createSql);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public void insert(Location loc, Player player) {
        if (conn == null) return;
        String insertSql = "INSERT INTO break_log (player_name, x, y, z, world) VALUES (?, ?, ?, ?, ?)";
        try (PreparedStatement pstmt = conn.prepareStatement(insertSql)) {
            pstmt.setString(1, player.getName());
            pstmt.setInt(2, loc.getBlockX());
            pstmt.setInt(3, loc.getBlockY());
            pstmt.setInt(4, loc.getBlockZ());
            pstmt.setString(5, loc.getWorld().getName());
            pstmt.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    // 关闭连接方法
    public void close() {
        try {
            if (conn != null && !conn.isClosed()) {
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

内容的提问来源于stack exchange,提问作者Dami 78

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 11:01:15