一个JVM内存调优案例

半年前一时兴起,跟风开发过一个贪吃蛇小游戏。只做了单机版,随后就失去了兴趣。项目地址:https://github.com/jmecn/Snake

这个项目在运行时,内存占用量非常大。起步就有100MB,随着运行时间增加,内存消耗越来越大。5分钟后差不多就消耗了1GB内存。

我猜测问题是由于Zay-ES工作时创建了大量的对象实例导致的。GC回收不及时,导致这些对象一直保留在堆内存中。因为这个原因,我认为Zay-ES框架有内存泄露(Memory Leak),最终因此放弃了这个项目。

学习JVM的工作方式之后,我才明白这不是Zay-ES的错。是我没有设置好JVM的堆内存,所以出现了这个结果。

祭出Visual VM,查看对内存的使用情况。

  • 蓝色线:已使用内存(Used Memory)
  • 黄色线:堆大小(Heap Space)

从上图中可以发现,这个程序运行时所需的最低内存大概只有20MB左右。由于没有设置堆的大小,JVM认为可以使用尽量多的内存(1GB),因此GC的执行频率很低。只有在已使用内存接近堆内存上限时,才执行一次GC,并扩大堆内存的上限。

上图中,已使用的内存峰值接近250MB,我决定将JVM堆的容量设置为256MB,看看结果会有什么不同。

-Xms256m -Xmx256m

GC的频率明显变高了,而且实际使用的内存只有不到128MB!这已经说明Zay-ES其实并没有内存泄露。

再度调整堆的大小,改为启动内存32MB,最大内存128MB。

-Xms32m -Xmx128m

这一次运行,GC的回收频率更高了,已使用内存的大小也更加稳定。

不过这个设置的GC频率太高,而且中途还有一次full gc,程序卡顿非常明显,游戏感受不太舒服。

于是我把启动内存提高到64MB,最大内存依然控制在128MB。

-Xms64m -Xmx128m

这次运行流畅多了,而且内存消耗也在接受范围内。