You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Libgdx无头应用中Mockito引发内存泄漏问题求助

解决LibGDX无头服务器运行数小时后OutOfMemoryError的问题

先明确你的场景:

我正在开发一款基于Libgdx的多人游戏,客户端与服务器均使用该框架。为使服务器以无头模式运行,我通过以下代码用Mockito模拟OpenGL上下文:

Gdx.gl = Mockito.mock(GL20.class, Mockito.withSettings().stubOnly()); 
new HeadlessApplication(new MainServer(true), config);

但发现服务器运行5~6小时后崩溃,报错信息为:Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "Headl...

结合LibGDX无头模式和Mockito的特性,我给你梳理几个可能的原因和对应的解决办法:

1. Mockito模拟对象的潜在内存泄漏

虽然你用了stubOnly(),但Mockito的mock对象默认还是会保留部分调用元数据,GL20接口方法数量多,长时间运行后这些累积的微小记录会积少成多,占用大量内存。

解决办法:替换为手动实现空GL20实例
放弃Mockito,自己写一个空的GL20实现,完全避免Mockito的额外开销:

Gdx.gl = new GL20() {
    // 实现GL20接口的所有方法,不需要的操作直接空实现,需要返回值的返回合理默认值
    @Override
    public void glClear(int mask) {}

    @Override
    public void glClearColor(float red, float green, float blue, float alpha) {}

    // 其他所有GL20方法按此方式补充
};
new HeadlessApplication(new MainServer(true), config);

这种方式没有Mockito的额外内存消耗,是无头服务器模拟GL上下文的更可靠方案。

2. HeadlessApplication的资源与线程管理问题

LibGDX的HeadlessApplication虽然是无头模式,但仍会启动后台线程(比如渲染循环线程),如果服务器逻辑中存在未正确释放的资源,或者线程持有对象引用导致GC无法回收,也会慢慢耗尽内存。

解决办法:

  • 检查服务器代码中是否加载了不必要的资源(比如纹理、音效),如果有,一定要在使用完后调用dispose()方法释放。
  • 配置JVM内存参数,临时缓解问题同时帮助排查:比如增加堆内存-Xmx2g(根据服务器硬件调整),同时开启GC日志-XX:+PrintGCDetails -XX:+PrintGCTimeStamps,通过日志观察内存增长趋势,判断是内存泄漏还是正常内存占用过高。
  • 检查HeadlessApplication的生命周期,确保服务器关闭或空闲时,正确调用dispose()清理资源。

3. 业务逻辑的内存泄漏

这是长时间运行服务最常见的问题:比如玩家断开连接后,对应的会话对象、数据缓存没从集合中移除,或者静态集合持有对象引用导致GC无法回收。

解决办法:

  • 使用内存分析工具(比如VisualVM、JProfiler)对运行中的服务器进行内存快照分析,找出占用内存最多的对象类型,定位泄漏点。比如发现大量PlayerSession对象存在,就检查玩家离线时是否没从玩家列表中删除。
  • 确保临时对象、会话对象在不需要时被及时移除,避免集合(比如HashMapArrayList)无限增长。
  • 检查是否有静态变量持有大量对象引用,比如静态玩家列表,这种情况会导致对象永远无法被GC回收。

总结

建议你先尝试替换Mockito的GL20 mock为手动实现的空实例,这是最快速排除潜在问题的方法。如果问题依然存在,再通过GC日志和内存分析工具定位业务逻辑或HeadlessApplication的内存泄漏点。

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

火山引擎 最新活动