频繁使用Integer.toString()转换导致的内存问题

4
我目前正在将一个使用C#(用于Windows Phone)开发的游戏移植到Java(Android)。
我们在Java版本中遇到了内存问题,经过剖析后,发现由于String对象的不可变性质,在内存中有大量的String对象,显然是由计分板方法引起的,每次得分改变时都会调用Integer.toString()方法(每秒多次)。我无法使用StringBuilder(这是C#版本中的工具),因为我们所使用的框架的文本渲染方法只接受String类型的参数,因此仍然会进行转换。
这在Java中是常见的问题吗?除了联系框架开发者修改他们的方法之外,有没有其他解决方案?
更新:
游戏非常快节奏,得分部分基于当前“阶段”开始以来流逝的时间。它每秒更新15次。
我们并未保留对字符串的引用,但我认为可能是框架在泄漏或复制这些字符串,因此我正在研究它(这不是公共框架,据我所知还没有被用于此类快节奏的游戏)。
池化技术是一个好建议,我考虑尝试一下,但必须修改记分系统以拥有固定的值。

你更新屏幕上的分数有多频繁? - Jon Skeet
你确定 Integer.toString() 是问题所在吗?通常情况下,除非你每秒钟这样做100次,否则不会有性能影响。 - Mr.Me
游戏一次显示多少分数?如果您在内存中有大量的字符串对象,但只需要其中一个或两个,则意味着应用程序正在泄漏字符串,并且垃圾收集器无法收集它们。请检查分析器以查看哪些对象引用了这些字符串,这可能会给您一些线索。 - Gorkem Ercan
也许你有一个内存泄漏,它保留了对这些字符串的引用。 - irreputable
2个回答

2

我不确定这个方法是否适用于你的情况,但是通常当你有一些固定的字符串值需要操作时,将它们全部添加到字符串池中是有意义的。在这种情况下,你可以强制JVM不为每个新字符串在堆上创建对象,而是利用字符串池。

你需要修改你的代码以从字符串池中返回字符串,例如:

return String.valueOf(123).intern();

以下是来自javadoc的一些额外解释:

当调用intern方法时,如果池中已经包含一个与此String对象相等的字符串(由equals(Object)方法确定),则返回池中的字符串。否则,将此String对象添加到池中,并返回对此String对象的引用。


这是一个好建议,但如果分数每次都变成新值,那么它可能不适用于这种情况。 - iTech
@iTech 你说得没错。从问题中并不清楚分数集合是否固定,坦白地说,如果没有一些分析信息,很难确定问题所在。 - Andrew Logvinov

0

我们最终通过创建自己的可修改字符串类,由固定长度字符数组支持,并编写了自己的文本渲染方法来解决了这个问题,一旦初始化后就没有任何分配。

之后,一切都运行得更加顺利,但我们仍然有几个由GC引起的“冻结”。在分析后,发现这是由于主游戏循环中在循环中创建大量迭代器造成的。然后,我们编写了一个使用迭代器池的自定义数组类,现在一切都很好!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接