避免Java中的缓存未命中

避免Java中的缓存未命中,java,caching,garbage-collection,Java,Caching,Garbage Collection,目前,我正在改变想法,开发更方便缓存的应用程序。 在C++中,我使用栈分配的方式,也可以用一个数组(数据驱动编程)来保存数据。 但我也是Java开发人员,有一个问题: 我听说Java是“缓存未命中生成器”。 所有东西都在堆中,在分配或垃圾收集器工作后分散在整个RAM中。我认为C#也存在同样的问题。 以数据驱动的方式编写Java有意义吗? 是否有任何方法可以优化Java代码,或者我们被Java自动优化和缓存未命中所困扰?您也可以在Java中提高缓存性能,但这涉及到它。基元类型的数组是连续的内存块,

目前,我正在改变想法,开发更方便缓存的应用程序。
在C++中,我使用栈分配的方式,也可以用一个数组(数据驱动编程)来保存数据。 但我也是Java开发人员,有一个问题:
我听说Java是“缓存未命中生成器”。
所有东西都在堆中,在分配或垃圾收集器工作后分散在整个RAM中。我认为C#也存在同样的问题。
以数据驱动的方式编写Java有意义吗?

是否有任何方法可以优化Java代码,或者我们被Java自动优化和缓存未命中所困扰?

您也可以在Java中提高缓存性能,但这涉及到它。基元类型的数组是连续的内存块,因此,只要您可以根据自己喜欢的内容重写代码。正如Stepanov所说,您可以用任何语言编写FORTRAN。我在过去看到过这种做法,但这并不好


另一方面,C#在这方面是一种更友好的语言
struct
类型具有连续的成员,因此您可以在C#中构建更高级别的缓存友好抽象,另外,值类型的
List
在单个连续内存块中分配
T

我的第一个建议是不要花太多时间担心它,除非您有未达到的特定性能目标。此外,在编写高效的Java代码时,还有许多其他途径需要探索

。。。但是,如果你真的想完成这条路径,你可以考虑“轻量级对象”模式(或轻量级):

原语的数组和类可能会占用连续的存储空间,因此您可以将这些对象用于底层存储,并使用位于顶部的适配器类返回数据的OO表示

您必须小心不要分配给许多适配器。如果一个适配器对象将引用传递到底层数据数组中,光标类型模式可能会很有用

<>在C++中,我使用栈分配的方式,也可以用一个数组(数据驱动编程)等… 在Java中,它将使用Escape分析自动在堆栈上放置短期的OBEJCT。我不会担心这一点,除非你在剖析器中看到这是一个问题。即使如此,分析器也可能阻止了逃逸分析的工作,这在实际程序中不是问题

我听说Java是“缓存未命中生成器”

<> > java比C++或C代码更具参考性,它是用嵌入对象内部的结构或对象编写的。这会产生多大的差异取决于应用程序对微调的敏感程度

所有东西都在堆中,在分配或垃圾收集器工作后分散在整个RAM中。我认为C#也存在同样的问题

Java(和C#)也不是一个随机内存安排程序。从理论上讲,物体可能在任何地方,但在实践中,它们通常不是。考虑一下你有没有;p>
class A { }

class B {
    A a = new A();
}
如果创建
B
a
可以在任何地方,但通常不是。当Java在Eden空间中分配内存时,它在内存中通常是连续的。这是分配内存最简单、最有效的方法。这意味着99.9%的时间,
A
将紧跟在
B
之后,可能在同一缓存线上。事实上,对于某些用例,“虚假共享”在Java中是一个真正的问题。i、 e.当您想要删除不在同一缓存线上的两个对象时

GC上会发生什么

在OpenJDK/Oracle JVM中,对象的复制顺序与发现顺序相反。i、 e.
A
在大多数情况下会出现在
B
之前

以数据驱动的方式编写Java有意义吗

这种情况就是这样,在<1%的情况下,这会产生很大的影响。然而,对于大多数代码,如果不是大多数应用程序,您将有很多更大的问题需要担心

有没有办法优化Java代码,或者我们被Java自动优化和缓存未命中所困扰

您可以使用
Unsafe
来控制您选择的内存结构。我们(编年史软件)的库允许您这样做,但即使我们希望您使用我们的服务,在99%的情况下,也没有理由担心这种微调。只有在极端情况下,它才会产生真正的影响

我不想修改垃圾收集器。但我知道它会复制周围的所有内容,所以会弄乱一点结构。我希望尽可能避免这种情况


这就是GC所做的。它将相关对象打包在一起,这不仅是为了提高效率,而且因为以找到对象的方式复制对象是最简单的实现。如果你想随机安排数据,你就必须有意识地这样做,这将是更多的工作。e、 g.如果你想避免“错误共享”,这是非常重要的。

你可以优化缓存未命中,但这涉及到处理人为复杂的类层次结构、大量数组,或者走
sun.misc.Unsafe
之路,这会增加自身的开销

Java对内存布局几乎没有保证。你有两个强有力的保证:

  • 超类的字段一起保存在内存中(可以避免错误共享,而无需诉诸
    sun.misc.confered
  • 数组在内存中是连续的
除了“不要太担心”之外,很难给出一般性的建议,但一般来说,简单的策略效果最好

尽可能使用
ArrayList
代替
LinkedList
HashMap
代替
TreeMap

如果您需要更多的控制,请使用开放寻址映射,特别是在处理基本类型(有一个fe)时