在托管代码中,如何实现良好的引用位置?

由于RAM似乎是新磁盘,并且由于该语句还意味着对内存的访问现在被认为是缓慢的,类似于磁盘访问一直以来的方式,因此我确实希望最大化高性能应用程序在内存中的引用位置。例如,在排序索引中,我希望相邻值接近(与哈希表中不同),并且我也希望索引指向的数据接近。

在C语言中,我可以用专门的内存管理器来构建数据结构,就像(非常复杂)Judy数组的开发人员所做的那样。通过直接控制指针,它们甚至在指针值本身中对其他信息进行编码。在Python,Java或C#中工作时,我故意将一个(或多个)抽象级别与这种类型的解决方案分开,并且我委托JIT编译器并优化运行时,在低级别上为我做聪明的把戏。

不过,我想,即使在这种高度抽象的情况下,也有一些东西在语义上可以被认为是“更接近”的,因此在低层次上实际上可能更接近。例如,我想知道以下内容(我在括号中的猜测):

  • 我可以期望数组是相邻的内存块吗(是)?
  • 同一实例中的两个整数是否比同一类的不同实例中的两个整数更接近(可能)?
  • 对象是否占用内存中的连续区域(否)?
  • 只有两个字段的对象数组和具有两个字段的单个对象之间有什么区别?(此示例可能是特定于 Java 的)intint[]

我开始在Java上下文中思考这些问题,但我的疑惑已经变得更加普遍,所以我建议不要将其视为Java问题。


答案 1
  • 在 .NET 中,数组的元素肯定是连续的。在Java中,我希望它们在大多数实现中都存在,但似乎不能保证。
  • 我认为可以合理地假设实例用于字段的内存位于单个块中...但不要忘记,其中一些字段可能是对其他对象的引用。

对于 Java 数组部分,Sun 的 JNI 文档包含以下注释,隐藏在有关字符串的讨论中:

例如,Java 虚拟机可能不会连续存储数组。

对于最后一个问题,如果你有两个,那么这些数组中的每一个都将是一个连续的内存块,但它们在内存中可能相距甚远”。如果您有一个包含两个 int 字段的对象数组,则每个对象彼此相距很远,但每个对象中的两个整数将彼此靠近。可能更重要的是,由于每个对象的开销,您最终将使用“大量对象”解决方案占用更多内存。在 .NET 中,您可以改用具有两个整数的自定义结构,并拥有一个这些整数的数组 - 这将把所有数据保存在一个大块中。int[]

我相信,在Java和.NET中,如果你在单个线程中快速连续地分配了很多小对象,那么这些对象很可能具有良好的引用位置。当 GC 压缩堆时,这可能会有所改善 - 或者如果堆

A B C D E

被压缩为

A D E B

(收集C的地方) - 突然之间,以前可能“接近”的A和B相距甚远。我不知道这是否真的发生在任何垃圾回收器中(周围有负载!),但这是可能的。

基本上,在托管环境中,您通常不像在非托管环境中那样能够控制引用的位置 - 您必须相信托管环境在管理它方面足够好,并且通过编码到更高级别的平台,您将节省足够的时间,以便将时间花在其他地方进行优化。


答案 2

首先,你的标题暗示了 C#。“托管代码”是微软创造的一个术语,如果我没有记错的话。

Java 基元数组保证是连续的内存块。如果您有

int[] array = new int[4];

你可以从JNI(本机C)得到一个指向实际数组的点。我认为这也适用于Array*类容器(ArrayList,ArrayBlockingQueue等)。int *p

我认为,JVM的早期实现将对象作为连续结构,但是使用较新的JVM无法假设这一点(JNI抽象了这一点)。

正如你所说,同一对象中的两个整数可能“更接近”,但它们可能不是。即使使用相同的JVM,这也可能会有所不同。

具有两个int字段的对象是一个对象,我不认为任何JVM都不能保证成员将“接近”。具有两个元素的 int 数组很可能由一个 8 字节长的数组支持。


推荐