首页 > 解决方案 > Java 8,重复字符串浪费了内存

问题描述

我正在调查在 Java 8 JVM 上运行的 Grails 3.3.10 服务器中的内存泄漏。我从内存不足的生产服务器获取堆转储,并使用JXRay对其进行分析。html 报告说,一些内存浪费在重复的字符串上,开销为 19.6%。其中大部分浪费在空字符串“”的重复上,并且主要来自数据库读取。我对此有两个问题。

  1. 我应该开始实习字符串还是操作成本太高不值得?

  2. 我的很多代码都处理来自 elasticsearch 的深度嵌套的 JSON 结构,我不喜欢代码的脆弱性,所以我创建了一个小的帮助类来避免在从 json 访问数据时出现拼写错误。

public static final class S {
    public static final String author      = "author";
    public static final String aspectRatio = "aspectRatio";
    public static final String userId      = "userId";
    ... etc etc

这有助于我避免这样的错别字:

    Integer userId = json.get("userid"); // Notice the lower case i. This returns null and fails silently
    Integer userId = json.get(S.userId); // If I make a typo here the compiler will tell me.

我对此感到相当高兴,但现在我在猜测自己。出于某种原因,这是一个坏主意吗?我还没有看到其他人这样做。这不应该导致创建任何重复的字符串,因为它们被创建一次,然后在我的解析代码中引用,对吧?

标签: javastringmemory-leaks

解决方案


String 持有类的问题是您使用的语言违背了它的语言设计。

类应该引入类型。没有提供实用程序的类型,因为它是“可以用字符串表示的所有内容”类型很少有用。虽然在许多程序中都会出现这种情况,但通常它们会引入比“所有东西都在这里”更多的行为。例如,语言环境数据库为不同语言提供替换字符串。

我会从制定合理的枚举开始。错误消息可能很容易转换为枚举,它具有简单的自动转换字符串表示。这样您就可以获得“错字检测”和内置分类。

 DiskErrors.DISK_NOT_FOUND
 Prompts.ASK_USER_NAME
 Prompts.ASK_USER_PASSWORD

像这样的改变的副作用可能会达到你想要的目标;但请注意,这些类型的更改通常表示可读性的丧失。

可读性不是你认为容易阅读的东西,而是从未使用过代码的人认为容易阅读的东西。

如果我看到“未找到您选择的硬盘驱动器”的问题,那么我将查看代码库中的字符串“未找到您选择的硬盘驱动器”。这可能会让我陷入两个地方:

  1. 在代码块中出现了错误消息。
  2. 在将该字符串映射到名称的表中。
  3. 在引发相同错误消息的许多代码块中。

通过表映射,我可以进行第二次搜索,搜索名称的使用位置。这可以让我遇到一些情况:

  1. 它在一个地方使用。
  2. 它在许多地方使用。

有了一个地方,就会出现一种代码维护问题。您现在拥有一个未被代码的任何其他部分使用的常量,该常量被维护在一个不靠近使用它的地方。这意味着要进行任何需要完全了解影响的更改,必须牢记远程常量的值,以了解逻辑更改是否应与更新的错误消息相结合。导致额外错误机会的不是错误消息的更新,而是它从正在处理的代码中删除的事实。

对于多个位置,我必须循环遍历所有匹配项,这与第一步中的多个字符串匹配基本相同。因此,该表并不能帮助我找到错误的根源,它只是添加了与解决问题无关的额外步骤。

现在,该表在一种情况下确实有一个明显的好处:当针对特定类型问题的所有消息应该同时更新时。问题是,这种情况很少见,而且不太可能发生。更有可能发生的是错误消息对于特定场景不够具体;但是,经过另一次“扫描所有使用它的地方”对于其他场景是正确的。因此,错误消息被拆分,而不是就地更新,因为查找表强制执行的耦合意味着如果不创建新的错误消息,就无法修改某些错误消息。

像这样的问题来自开发人员在吸引开发人员的功能中滑倒。在您的情况下,您正在构建一个反错字系统。让我提供一个更好的解决方案;因为错别字是真实的,也是一个真正的问题。

编写一个单元测试来捕获预期的输出。您很少会以完全相同的方式写两次相同的错字。是的,这是可能的,但协调的拼写错误会对两个系统产生相同的影响。如果您在查找表中引入拼写错误,并在使用中引入它,好处将是一个工作程序,但很难称其为高质量的解决方案(因为拼写错误没有受到保护并且存在于复制)。

在将代码提交到构建系统之前对其进行审查。评论可能会失控,尤其是对于不灵活的评论者,但好的评论应该评论“你拼错了”。如果可能的话,作为一个团队来审查代码,这样你就可以在他们发表评论时指出你的想法。如果你很难与人合作(或者他们很难与人合作),你会发现同行评审很困难。如果发生这种情况,我很抱歉,但如果你能获得良好的同行评审,这是对这些问题的第二“最佳”防御。

很抱歉这个回复的长度,但我希望这能让您有机会记住从解决方案中“退后一步”,看看它如何影响您对代码的未来操作。

至于""字符串,关注为什么要设置它可能比用实习修补问题更有效地构建更好的产品(但我无权访问您的代码库,所以我可能错了!)

祝你好运


推荐阅读