slb*_*uce 7 java memory string profiling retained-in-memory
这是一个我们难以理解的问题.用文字描述它是很棘手的,但我希望能够理解这个要点.
我知道字符串的实际内容包含在内部char数组中.在正常情况下,字符串的保留堆大小将包括40个字节加上字符数组的大小.这在这里解释.调用子字符串时,字符数组保留对原始字符串的引用,因此字符数组的保留大小可能比字符串本身大很多.
但是,当使用Yourkit或MAT分析内存使用情况时,似乎会发生奇怪的事情.引用char数组保留大小的字符串不包括字符数组的保留大小.
一个例子如下(半伪代码):
String date = "2011-11-33"; (24 bytes)
date.value = char{1172}; (2360 bytes)
Run Code Online (Sandbox Code Playgroud)
字符串的保留大小定义为24个字节,不包括字符数组的保留大小.如果由于许多子字符串操作而导致对字符数组的引用很多,这可能是有意义的.
现在,当此字符串包含在某种类型的集合(如数组或列表)中时,此数组的保留大小将包括所有字符串的保留大小,包括字符数组的保留大小.
然后我们有这样的情况:
Array's retained size = 300 bytes
array[0] = String 40 bytes;
array[1] = String 40 bytes;
array[1].value = char[] (220 bytes)
Run Code Online (Sandbox Code Playgroud)
因此,您必须查看每个数组条目,以尝试确定保留大小的来源.
同样,这可以解释为数组包含所有保存对同一字符数组的引用的字符串,因此数组的保留大小总是正确的.
现在我们解决了这个问题.
我在一个单独的对象中保留了对上面讨论的数组的引用以及具有相同字符串的不同数组.在两个数组中,字符串引用相同的字符数组.这是预期的 - 毕竟我们正在讨论相同的字符串.但是,此字符数组的保留大小将计入此新对象中的两个数组.换句话说,保留的大小似乎是双倍的.如果我删除第一个数组,那么第二个数组仍将保持对字符数组的引用,反之亦然.这引起了混淆,因为似乎java对同一个字符数组持有两个单独的引用.怎么会这样?这是java内存的问题还是仅仅是分析器显示信息的方式?
这个问题让我们在尝试追踪应用程序中的大量内存使用时遇到了很多麻烦.
再次 - 我希望那里的人能够理解这个问题并解释它.
谢谢你的帮助
我在一个单独的对象中保留了对上面讨论的数组以及具有相同字符串的不同数组的引用。在两个数组中,字符串引用相同的字符数组。这是预料之中的——毕竟我们谈论的是同一个字符串。但是,此新对象中的两个数组都会计算此字符数组的保留大小。换句话说,保留的大小似乎是两倍。
这里有一个支配树中的传递引用:
字符数组不应显示在任一数组的保留大小中。如果探查器以这种方式显示它,那么就会产生误导。
这是JProfiler在最大对象视图中显示这种情况的方式:
两个数组中包含的字符串实例显示在数组实例外部,并带有 [transitive reference] 标签。如果你想探索实际的路径,你可以将数组持有者和字符串添加到图中并找到它们之间的所有路径:
免责声明:我的公司开发 JProfiler。