Oop Scala中对象引用的成本是多少?

Oop Scala中对象引用的成本是多少?,oop,scala,jvm,Oop,Scala,Jvm,假设我们构建一个对象来表示某种网络(社交、无线等)。所以我们有一些“节点”对象来表示网络的类型,不同的节点可能有不同的行为等等。网络有一个可变的节点列表 但是每个节点都有邻居,这些邻居也是节点。因此,在某个地方,每个节点都必须有一个该节点所有邻居的列表——或者,无论何时需要,都必须动态生成这样一个列表。如果邻居列表存储在节点对象中,那么(a)将其存储为节点列表,还是(b)将其存储为可用于引用网络外节点的编号列表,成本更低 为了清晰起见,有一些代码: //approach (a) class n

假设我们构建一个对象来表示某种网络(社交、无线等)。所以我们有一些“节点”对象来表示网络的类型,不同的节点可能有不同的行为等等。网络有一个可变的节点列表

但是每个节点都有邻居,这些邻居也是节点。因此,在某个地方,每个节点都必须有一个该节点所有邻居的列表——或者,无论何时需要,都必须动态生成这样一个列表。如果邻居列表存储在节点对象中,那么(a)将其存储为节点列表,还是(b)将其存储为可用于引用网络外节点的编号列表,成本更低

为了清晰起见,有一些代码:

//approach (a)

class network {
  val nodes = new MutableList[Node]
  // other stuff //
}

class Node {
  val neighbors = new MutableList[Node]
  // other stuff //
}

//approach (b)
class Network {
  val nodes = new MutableList[Node]
  val indexed_list = //(some function to get an indexed list off nodes)
//other stuff//
}

class Node {
  val neighbors = MutableList[Int]
//other stuff//
}

方法(a)似乎是最简单的。我的第一个问题是,这在Scala 2.8中是否昂贵,第二个问题是它是否违反了干燥的原则?

问题是——什么样的成本?在内存方面,b)方法可能会消耗更多内存,因为您在该列表中既有可变列表,又有装箱整数,还有另一个保存所有索引的全局结构。此外,它可能会更慢,因为您需要几个级别的间接寻址才能到达相邻节点

一个重要的注意事项是,一旦开始将整数存储到可变列表中,它们就会进行装箱。因此,在这两种情况下都有一个堆对象列表。为了避免这种情况,而且为了节省内存,在b)方法中,您必须保留一个动态增长的整数数组,这些整数是邻居的索引

现在,即使您按照上述建议修改方法b),并确保
网络
类中的索引列表确实是一个有效的结构(直接查找表或哈希表),您仍然需要支付间接成本来查找
节点
。内存消耗仍然会更高。我看到的唯一好处是,如果担心内存不足,可以保留某种弱引用表,并在需要时重新创建
节点
对象,但无法在保留一组弱引用的
索引列表中找到它

当然,这只是一个假设,您必须对代码进行概要分析/基准测试才能看到差异

我的建议是在
Node
中使用类似于
ArrayBuffer
的东西,并使用它存储对节点的直接引用


如果内存问题是一个问题,并且您希望将b)方法与弱引用结合使用,那么我将进一步建议为邻居滚动您自己的动态增长整数数组,以避免使用
ArrayBuffer[Int]装箱

简短回答:过早优化是etc的根源。请使用干净的参考方法。当您遇到性能问题时,没有什么可以替代评测和基准测试

详细回答:Scala使用与Java完全相同的引用机制,因此这实际上是一个JVM问题,而不是Scala问题。从形式上讲,JVM规范对如何实现引用只字未提。实际上,它们往往是单词大小或更小的指针,指向对象或指向对象的表的索引(后者有助于垃圾收集器)

无论哪种方式,ref数组的大小与32位vm上的int数组的大小大致相同,或者是64位vm上的两倍(除非使用压缩oop)。这种加倍可能对你很重要,也可能不重要

如果使用基于ref的方法,则从节点到邻居的每次遍历都是一个引用间接寻址。使用基于int的方法,从节点到邻居的每次遍历都是对表的查找,然后是间接引用。因此int方法的计算成本更高。假设你把整数放在一个集合中,这个集合不包含整数。如果你把整数框起来,那就太疯狂了,因为现在你有了和原来一样多的引用,你有了一个表格查找

无论如何,如果您使用基于引用的方法,那么额外的引用会为垃圾收集器带来一些额外的工作。如果对节点的唯一引用位于一个数组中,那么gc将非常快地扫描它。如果它们分散在一个图中,那么gc将不得不更加努力地跟踪它们。这可能会也可能不会影响你的需求

从清洁度的角度来看,基于ref的方法要好得多。所以,跟着它走,然后配置文件,看看你在哪里花费你的时间。这两种方法都可以