Parallel GC and array object layout: way off the base and laid out in reverse?
aleksey.shipilev at oracle.com
Wed Sep 4 04:28:45 PDT 2013
On 09/04/2013 02:56 PM, Thomas Schatzl wrote:
> Also the results are wrong:
>>> $ java -XX:+UseParallelGC ArrayLayoutTest
>>> Before the GC:
>>> array is at 4120951026 (0 units off base)
>>> object is at 4120951033, 7 units off base, toString = 0
> ^^^ the first array element is typically on
> word offset 3 or so... (iirc in the simplest case: 1 word header, 1 word
> klass pointer, 1 int element size and possibly some padding).
That is not &arr[i], i.e. not the (arr + i*sizeof(oop)). This is the
location of object referred by the arr[i], i.e. (long)arr[i]. The issue
is about GC laying out the referenced objects in the reverse order.
I had updated the test one more time to make in clearer. It does not
uses either Integer or Strings to dodge any sort of magic VM can do
This is the sample output:
$ ~/Install/jdk8b104/bin/java -XX:-UseCompressedOops ArrayLayoutTest
Before the GC:
array is at 140679387257752 (0 units off base)
object is at 140679387257856, 104 units off base, toString = 0
object is at 140679387257880, 128 units off base, toString = 1
object is at 140679387257904, 152 units off base, toString = 2
object is at 140679387257928, 176 units off base, toString = 3
object is at 140679387257952, 200 units off base, toString = 4
object is at 140679387257976, 224 units off base, toString = 5
object is at 140679387258000, 248 units off base, toString = 6
object is at 140679387258024, 272 units off base, toString = 7
object is at 140679387258048, 296 units off base, toString = 8
object is at 140679387258072, 320 units off base, toString = 9
Without the compressed oops, 1 unit = 1 byte.
Which means that for freshly allocated object, array of 10 references
takes 80 bytes, plus 24 bytes for the header (8+8 headers, 4 array size,
4 for alignment), totalling 104 bytes. Right after the array, we start
to layout the referenced objects, which take 24 bytes each (16 bytes for
the header + 4 bytes int + 4 bytes alignment up to 8 bytes). Notice how
dense they are packed.
After the GC:
array is at 140676601743672 (0 units off base)
object is at 140676601743992, 320 units off base, toString = 0
object is at 140676601743968, 296 units off base, toString = 1
object is at 140676601743944, 272 units off base, toString = 2
object is at 140676601743920, 248 units off base, toString = 3
object is at 140676601743896, 224 units off base, toString = 4
object is at 140676601743872, 200 units off base, toString = 5
object is at 140676601743848, 176 units off base, toString = 6
object is at 140676601743824, 152 units off base, toString = 7
object is at 140676601743800, 128 units off base, toString = 8
object is at 140676601743776, 104 units off base, toString = 9
Now it's different. We know the array and all the referenced values got
promoted, because their addresses changed. But now, we see the
referenced objects are laid out in reverse! What gives?
> Looking at the code in more detail, addressOf() simply seems to return
> the address value of the element. Which may be anywhere.
Yes, and user code should be oblivious to this. However, I ask the
different question: whether we should lay out the referenced elements in
their indexed order, not in reverse.
> I.e. arr[i] instead of &arr[i] when using C notation.
addressOf() is somewhat tricky. We actually store the oop into the
Object, and then read that oop as the plain int/long. Which gets us
the (long)arr[i], not really the &arr[i].
More information about the hotspot-gc-dev