这是一个我们很难理解的问题。用文本来描述它是很棘手的,但我希望它的要点能被理解。
我知道字符串的实际内容是包含在内部char数组中的。在正常情况下,字符串的保留堆大小将包括40个字节加上字符数组的大小。here解释了这一点。在调用子字符串时,字符数组保留了对原始字符串的引用,因此字符数组保留的大小可能比字符串本身大得多。
但是,在使用Yourkit或MAT分析内存使用情况时,似乎会发生一些奇怪的事情。引用字符数组的保留大小的字符串不包括字符数组的保留大小。
示例如下(半伪码):
String date = "2011-11-33"; (24 bytes)
date.value = char{1172}; (2360 bytes)字符串的保留大小被定义为24字节,不包括字符数组的保留大小。如果由于许多子字符串操作而有许多对字符数组的引用,这可能是有意义的。
现在,当此字符串包含在某种类型的集合中时,例如数组或列表,则此数组的保留大小将包括所有字符串的保留大小,包括字符数组的保留大小。
然后我们就会遇到这样的情况:
Array's retained size = 300 bytes
array[0] = String 40 bytes;
array[1] = String 40 bytes;
array[1].value = char[] (220 bytes)因此,您必须查看每个数组条目,以尝试找出保留的大小来自何处。
同样,这可以解释为该数组包含所有包含对同一字符数组的引用的字符串,因此该数组的保留大小是正确的。
现在我们来看看问题所在。
我在一个单独的对象中保留了一个对上面讨论的数组的引用,以及一个具有相同字符串的不同数组。在这两个数组中,字符串引用相同的字符数组。这是意料之中的--毕竟我们讨论的是同一个字符串。但是,对于这个新对象中的两个数组,都会计算该字符数组的保留大小。换句话说,保留的大小似乎是两倍。如果我删除了第一个数组,那么第二个数组仍然保留对字符数组的引用,反之亦然。这造成了混淆,因为java似乎持有对同一字符数组的两个单独的引用。这怎么可能呢?这是java内存的问题,还是分析器显示信息的方式?
这个问题给我们带来了很多令人头疼的问题,因为我们试图跟踪应用程序中的巨大内存使用情况。
再说一次-我希望外面的人能够理解这个问题并解释它。
谢谢你的帮忙
发布于 2011-12-08 18:52:46
我在一个单独的对象中保存了一个对上面讨论的数组的引用,以及一个具有相同字符串的不同数组。在这两个数组中,字符串引用相同的字符数组。这是意料之中的--毕竟我们讨论的是同一个字符串。但是,对于这个新对象中的两个数组,都会计算该字符数组的保留大小。换句话说,保留的大小似乎是两倍。
您在这里得到的是支配树中的可传递引用

字符数组不应显示在任一数组的保留大小中。如果分析器以这种方式显示它,那么就会产生误导。
这是JProfiler在最大的对象视图中显示这种情况的方式:

包含在两个数组中的字符串实例显示在数组实例的外部,带有一个可传递的引用标签。如果你想探索实际的路径,你可以将数组持有者和字符串添加到图中,并找到它们之间的所有路径:

免责声明:我的公司开发JProfiler。
发布于 2011-12-08 16:11:32
我想说这只是分析器显示信息的方式。它不知道应该将这两个阵列视为“重复数据消除”。如何将这两个数组包装到某种虚拟容器对象中,并对其运行分析器?然后,它应该能够处理“重复计数”。
发布于 2011-12-08 16:10:36
除非字符串被截留,否则它们可以是equal(),但不能是==。当从char数组构造String对象时,构造函数将复制char数组。(这是保护不可变字符串不受以后char数组值更改影响的唯一方法。)
https://stackoverflow.com/questions/8427892
复制相似问题