Java Object[]和缓存交易

4

我们知道,当内存被移动到CPU的L缓存时,它是通过缓存行进行移动的,因此整个缓存替换性能优化...

在Java中,如果我们定义一个数组,JMM保证为每个元素分配的内存将按顺序分配。但是,如果我们有一个引用数组,则这些引用可以随机指向内存中的不同位置。

我的问题是Java是否按顺序分配实际对象内存?我们有哪些底层优化?

例如,如果我们声明int[],我们可以确信它们实际上都是按顺序在内存中的,但如果我们定义一个NewType(类似于结构),其中有两个int字段,并声明NewType[],那么Java会找出并保持实际内存的顺序吗?


也许我错了。内存分配JVM不能混合使用。JVM根据需要进行动态内存分配(从技术上讲,这是因为在那个时间点资源可用性最好)。 - Srinath Ganesh
1
一个单独的对象会在一个单独的块中分配。与此相关的是GC是深度优先还是广度优先复制。这两种变体都存在于Hotspot中。在这里,您可以找到一些相关信息:http://www.oracle.com/technetwork/server-storage/ts-6434-159339.pdf - eckes
不太明白“GC是深度优先还是广度优先复制”的意思,你能否提供一些相关链接吗?(我已经搜索过了,但没有找到合适的结果) - vach
2个回答

3
我的问题是,Java是否按顺序分配实际对象内存?大部分情况下,OpenJDK/Oracle JVM确实会这样做。有时不会的情况包括:在tenured space中分配大型对象时;您的TLAB已满并且需要获取另一个TLAB。然而,在TLAB内部,它只是按顺序在内存中分配。声明NewType[]时,Java不会强制执行任何操作,也不会随意在内存中随机分配对象。通常情况下,每个new对象都会紧跟在上一个对象后面。

1
只是提一下 - 垃圾回收器可能会移动已分配的对象,因此即使它们最初是顺序分配的,也不能保证它们将永远保持顺序。 - Svetlin Zarev
2
@SvetlinZarev 是的,然而垃圾回收器并不会费尽心思地将对象随机排列在内存中。相反,发现顺序通常决定了局部性。 - Peter Lawrey
这意味着无论我们做什么,如果我们有引用类型的数组,我们都会失去CPU缓存友好性吗? - vach
1
我的意思是,我们可能有一个跨越两个连续G1GC区域的LinkedList。 GC周期可能仅从第一个区域收集垃圾,然后将剩余元素疏散到内存中的另一个区域,因此现在不是所有元素都是连续的。 - Svetlin Zarev
@SvetlinZarev 正确,但大多数情况下,它们在性能方面的差异微乎其微。 - Peter Lawrey

1
但如果我们定义一个类似于结构体的NewType,其中有两个int字段,并声明NewType [],Java会找出并保留实际内存顺序吗?
在这种情况下,Java不太友好,因为除了原始类型之外,Java数组不是紧密打包的数据结构,它们是指向在内存中其他位置分配的对象的引用数组。
也就是说,从数组到对象本身将至少存在一级间接性。这个问题通常被称为“指针追踪”。
也就是说,通常内存布局看起来像这样:
HlRRRRRRRRRRRRRRRRRRRRRRRRR0HR0iii0HR0iii0HR0iii0HR0iii0HR0iii0HR0iii0HR0iii0
         Array             | Obj  | Obj  | Obj  | Obj  | Obj  | Obj  | Obj  |

H = object header
l = array length
R = reference
i = int
0 = various types of padding

你可以使用jol来检查对象的内存布局。
JDK开发人员正在研究值类型,作为Valhalla项目的一部分,这将最终允许存在紧凑数组,这可能是Panama项目的一部分,但这仍然遥远。
与此同时,有第三方项目旨在提供类似的功能:

其他项目要么使用离堆存储(例如通过sun.misc.Unsafe),要么使用对ByteBuffer / byte[]数组的视图来创建紧凑、高缓存友好的数据结构,但代价是更复杂的API。


哇,谢谢!这些信息非常有用 :) 我之前完全不知道 JOL。 - vach

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接