不可变类的存储区域

Ank*_*kur 0 java immutability

在最近的采访中,有人问我字符串是否存储在字符串池中,因为它支持不变性,那么我们的自定义不可变类存储在 java 中的什么位置。

我给出了以下解释——所有的类变量原语或对象引用(它只是一个指向对象存储位置的指针,即堆)也存储在堆中。类加载器加载的类和静态变量和静态对象引用存储在永久生成的堆中的特殊位置。

但是面试官一直在争论——如果String有字符串池,那么不可变类是否也有这样的概念?

请谁能解释一下不可变类的存储区域,因为它们也像 string 一样不可变?

Hol*_*ger 5

在最近的采访中,有人问我字符串是否存储在字符串池中,因为它支持不变性,那么我们的自定义不可变类存储在 java 中的什么位置。

这是一个带有错误前提的荒谬问题。字符串不是“存储在字符串池中”,而是像任何其他对象一样存储在堆中。这是一种同义反复,因为堆内存被精确定义为“堆是运行时数据区域,所有类实例和数组的内存都从该区域分配。

字符串池可以被视为包含字符串,就像 aCollection可能包含对象一样,但在任何一种情况下,它都只是保存对对象的引用。因此,根据定义,池中包含的字符串仍存储在堆内存中,而池有对其的引用。

但是面试官一直在争论——如果String有string-pool,那么immutable classes还能有这样的概念吗?

那是一个完全不同的问题。当然,您可以实现一个对象池,正如Collection上面的类比已经表明的那样。就像池中包含的字符串仍然存储在堆内存中一样,当被用于池的任何数据结构引用时,类的对象仍然存储在堆内存中。甚至不需要对象是不可变的,拥有这样一个池,但是当可能发生突变时,隐含的实例共享会导致语义问题。所以创建一个池通常只对不可变对象有意义。

例如,大量的包装类的有这样的共享,valueOf为方法ShortIntegerLong将在返回共享实例为值-128 … +127范围和允许实现更共享,而ByteBoolean返回共享实例为所有可能的值。

但是,并非每个不可变类都为其所有值实现池是有原因的:

  • 如果有较大的值空间,则必须考虑支持对未使用对象进行垃圾回收,即使是在被池引用时
  • 您必须考虑池的线程安全性
  • 上述两点可能会导致一个复杂的解决方案,当对象只使用很短的时间时,您不想支付性能损失,因为共享只会减少长期存活对象的内存消耗

这也适用于现有示例。包装对象只为有限的值空间提供共享对象。哪些是预先分配的并且从不GCed。另一方面,字符串池是动态的、线程安全的并且支持对其元素进行垃圾回收,这就是为什么intern()它不是一个廉价的操作并且不应应用于每个字符串的原因。相反,池主要用于常量,它们确实是长期存在的。