Minecraft Spigot插件启用卡住并抛出OutOfMemoryError问题求助
问题原因
1. 直接诱因:文件读取死循环 (核心问题)
你代码里的readInfections方法存在逻辑错误,是导致启动时OOM的直接原因:
你仅在进入循环前调用了一次input.nextLine()读取第一行内容,循环体内没有读取新行的逻辑,只要文件第一行不为空,就会无限循环解析同一行数据,往infections列表中反复添加对象,直到内存耗尽。
2. 内存泄漏隐患:感染记录无限制累加
- 你的
batCovid方法每10tick就会扫描一次玩家附近的蝙蝠,只要检测到蝙蝠就会往列表里新增一条感染记录,同一个玩家会被反复添加,没有去重逻辑 - 没有过期记录清理逻辑,即使玩家感染天数归零也不会从列表中移除,列表占用的内存会随着运行时间持续增长
3. 静态集合的额外风险
infections被声明为静态变量,如果你使用服务器热重载功能,旧类加载器持有的集合实例无法被GC回收,会进一步累积内存泄漏。
修复方案
1. 修复文件读取死循环
修改readInfections方法,每次循环读取新行,使用hasNextLine判断是否还有待读取内容:
public void readInfections() throws FileNotFoundException { File file = new File("infected.txt"); if(file.length() == 0) { return; } try (Scanner input = new Scanner(file)) { while (input.hasNextLine()) { String line = input.nextLine().trim(); if (line.isEmpty()) { break; } infections.add(parseInfectionLine(line)); } } }
2. 新增感染去重和过期清理逻辑
修改batCovid方法,避免重复添加同一个玩家的感染记录,同时定期清理到期的记录:
public void batCovid() { // 先清理已经到期的感染记录 infections.removeIf(infection -> infection.getDays() <= 0); for(Player player : Bukkit.getOnlinePlayers()) { // 判断玩家是否已经处于感染状态,避免重复添加 boolean isInfected = infections.stream() .anyMatch(infection -> infection.getUuid().equals(player.getUniqueId())); if (isInfected) { continue; } // 检测附近是否有蝙蝠 for(Entity nearby : player.getNearbyEntities(6, 6, 6)) { if (nearby instanceof Bat) { infections.add(new CovidInfection(player.getUniqueId(), player.getName(), 14)); break; } } } }
3. 优化静态变量声明
由于Spigot插件本身是单例运行的,不需要将infections声明为静态变量,修改为实例变量即可避免热重载带来的内存泄漏:
把原代码中
private static ArrayList<CovidInfection> infections;
修改为
private ArrayList<CovidInfection> infections;
对应调整getInfected方法为实例方法,调用时通过插件实例获取即可。
内容的提问来源于stack exchange,提问作者joshg820
相关产品推荐
相关产品推荐

