Java8,重复字符串浪费内存
我正在调查运行在Java8JVM上的Grails3.3.10服务器中的内存泄漏。我从一台内存不足的生产服务器上获取了一个堆转储,并使用进行了分析。html报告说一些内存浪费在重复字符串上,开销为19.6%。其中大部分都浪费在空字符串“”的副本上,并且大部分来自数据库读取。关于这一点,我有两个问题Java8,重复字符串浪费内存,java,string,memory-leaks,Java,String,Memory Leaks,我正在调查运行在Java8JVM上的Grails3.3.10服务器中的内存泄漏。我从一台内存不足的生产服务器上获取了一个堆转储,并使用进行了分析。html报告说一些内存浪费在重复字符串上,开销为19.6%。其中大部分都浪费在空字符串“”的副本上,并且大部分来自数据库读取。关于这一点,我有两个问题 我应该开始实习字符串,还是手术成本太高而不值得 我的代码中有相当一部分涉及elasticsearch中的深度嵌套JSON结构,我不喜欢代码的脆弱性,因此我创建了一个小的帮助器类,以避免从JSON访问数
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.
我对此相当高兴,但现在我在猜测自己。出于某种原因,这是个坏主意吗?我没见过其他人这样做。这不会导致创建任何重复字符串,因为它们只创建一次,然后在我的解析代码中引用,对吗
问题1:我应该开始实习字符串,还是一次手术的成本太高而不值得
如果没有关于字符串是如何创建的以及它们的典型生存期的更多信息,很难说,但是一般的答案是否定的。这通常是不值得的
(实习也不能修复你的内存泄漏。)
以下是一些原因(恐怕有点手舞足蹈):
- 插入字符串不会阻止正在插入的字符串被创建。您的代码仍然需要创建它,GC仍然需要收集它
- 有一个隐藏的数据结构来组织插入的字符串。这需要内存。它还需要CPU来检查一个字符串是否在内部数据结构中,并在需要时添加它
- GC需要对内部数据结构执行特殊(弱引用)操作,以防止其泄漏。那是一笔开销
- 固定的绳子比非固定的绳子寿命更长。它更有可能被保留到“旧”堆中,这会导致其寿命延长更长。。。因为“旧”堆被GC’ed的次数较少
- 你真的节省了内存吗
- 这会影响GC运行的速率吗
- 这会影响GC暂停吗
- 它是否会影响请求时间/吞吐量
出于某种原因,这是个坏主意吗?这不会导致创建任何重复字符串,因为它们只创建一次,然后在我的解析代码中引用,对吗 我想不出任何理由不那样做。它当然不会直接导致创建重复字符串
另一方面,这样做并不能简单地减少字符串重复。表示文字的字符串会自动插入。字符串持有类的问题在于,您使用的语言与其语言设计不符 类应该引入类型。一个不提供任何实用程序的类型,因为它是“用字符串可以说的一切”类型,很少有用。虽然在许多程序中都会出现这种情况,但它们通常会引入比“所有东西都在这里”更多的行为。例如,语言环境数据库为不同的语言提供替换字符串 我首先要做一些合理的列举。错误消息可能很容易转换为枚举,枚举具有简单的自动转换字符串表示形式。这样你就可以得到你的“打字错误检测”和一个内置的分类
DiskErrors.DISK_NOT_FOUND
Prompts.ASK_USER_NAME
Prompts.ASK_USER_PASSWORD
这种变化的副作用可以达到你想要的目标;但要小心,这些变化往往意味着可读性的丧失
可读性不是你认为容易阅读的东西,而是从未使用过代码的人认为容易阅读的东西
如果我发现“您选择的硬盘未找到”有问题,那么我会在代码库中查找字符串“您选择的硬盘未找到”。这会让我在两个地方落脚:
DiskErrors.DISK_NOT_FOUND
Prompts.ASK_USER_NAME
Prompts.ASK_USER_PASSWORD