JDK-8069760 : When iterating over a card, G1 often iterates over much more references than are contained in the card
  • Type: Bug
  • Component: hotspot
  • Sub-Component: gc
  • Affected Version: 9
  • Priority: P2
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2015-01-21
  • Updated: 2015-09-29
  • Resolved: 2015-02-02
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8 JDK 9
8u60Fixed 9 b51Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Description
In HeapRegion::oops_on_card_seq_iterate_careful() there is the following code that actually iterates over the card contents (stripped of non-essential stuff):

(1)
  if (!g1h->is_obj_dead(obj)) {
    obj->oop_iterate(cl, mr);
  }

  while (cur < end) {
    obj = oop(cur);
    if (obj->klass_or_null() == NULL) { return cur; };

    // Otherwise:
    next = cur + block_size(cur);
(2)
    if (!g1h->is_obj_dead(obj)) {
      if (next < end || !obj->is_objArray()) {
        // This object either does not span the MemRegion
        // boundary, or if it does it's not an array.
        // Apply closure to whole object.
        obj->oop_iterate(cl);
      } else {
        // This obj is an array that spans the boundary.
        // Stop at the boundary.
        obj->oop_iterate(cl, mr);
      }
    }
    cur = next;
  }

I.e. at (1) the code iterates over the first object reaching into the card, always limiting itself to the card area. Then, without advancing the "cur" pointer, it immediately iterates over the same object either limited to the card or without limit.

This is because non-object arrays may be marked imprecise, i.e. only at the object header. So you need to always iterate over regular objects reaching into or out of the area in full.

The side effect is, that the first object will always be iterated for references twice. This is bad because particularly for parts of object arrays G1 does duplicate work, lengthening the update rs phase for no reason. Particularly loads with many reference arrays benefit from a fix.

By rearranging the code, transforming the if-while construct into a do-while loop this can be avoided completely. (And being more compact and more easily understood).

Fix this. A follow-up might want to investigate what benefit this imprecise marking actually gives.
Comments
Noreg-hard justification: - this is a performance-only regression, apart from longer pause times there is no difference in actual execution. It is very hard to reliably test for that - normal execution of applications (with verification) will quickly find out that references are missing in the remembered set (as noticed during development)
27-01-2015

Non-major performance regressions are P2 per default. Increasing prio.
26-01-2015

Note that both of these issues are a huge performance problems for programs using larger object arrays. E.g. the test program in JDK-8062128 halves its runtime (still a good way away from CMS), other benchmarks like nosql seem to improve a lot in throughput too.
22-01-2015

There is another bug that objArrayOops are iterated in full if they reach into the card. The problem is the || operator in if (next < end || !obj->is_objArray()) { which is also true for object arrays reaching into the given memory region.
22-01-2015

History: The problem with the duplicate iteration of the first object reaching into the card is a day one problem. The second has been introduced with JDK-7133038.
22-01-2015