From 45d36eac83789d2006739e7d98f85eca81082bb9 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Fri, 31 Jan 2020 18:23:50 +0300 Subject: [PATCH] C: fix crash on GC when there are queued WeakReference instances in the heap When performing young GC, we can encounter some WeakReferences which get into corresponding ReferenceQueue. In this case queue internal state will be updated. After it defragmentation phase runs which updates references to relocated objects. For performance reason, defragmentation phase scans only regions either marked with write barriers or regions that contain surviving objects in young generation. However, sometimes a queue can be in neither of these sets, so we additionally mark regions containing all affected queues. --- core/src/main/java/org/teavm/runtime/GC.java | 15 ++++++++++++--- .../resources/org/teavm/backend/c/heaptrace.c | 10 ++++++++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/teavm/runtime/GC.java b/core/src/main/java/org/teavm/runtime/GC.java index f33172cd9..0d670f589 100644 --- a/core/src/main/java/org/teavm/runtime/GC.java +++ b/core/src/main/java/org/teavm/runtime/GC.java @@ -438,7 +438,9 @@ public final class GC { hasObjectsFromYoungGen |= enqueueMark(object.object); } } - if (object.next == null && object.object != null) { + if (object.next != null) { + hasObjectsFromYoungGen |= enqueueMark(object.next); + } else if (object.object != null) { object.next = firstWeakReference; firstWeakReference = object; } @@ -448,9 +450,8 @@ public final class GC { private static boolean markReferenceQueue(RuntimeReferenceQueue object) { RuntimeReference reference = object.first; boolean hasObjectsFromYoungGen = false; - while (reference != null) { + if (reference != null) { hasObjectsFromYoungGen |= enqueueMark(reference); - reference = reference.next; } return hasObjectsFromYoungGen; } @@ -519,14 +520,22 @@ public final class GC { queue.first = reference; } else { queue.last.next = reference; + makeInvalid(queue.last); } queue.last = reference; + makeInvalid(queue); } } reference = next; } } + private static void makeInvalid(RuntimeObject object) { + long offset = object.toAddress().toLong() - heapAddress().toLong(); + Address cardTableItem = cardTable().add(offset / regionSize()); + cardTableItem.putByte((byte) (cardTableItem.getByte() & ~CARD_VALID)); + } + private static void sweep() { MemoryTrace.sweepStarted(); diff --git a/core/src/main/resources/org/teavm/backend/c/heaptrace.c b/core/src/main/resources/org/teavm/backend/c/heaptrace.c index fcf8107d9..6037870dc 100644 --- a/core/src/main/resources/org/teavm/backend/c/heaptrace.c +++ b/core/src/main/resources/org/teavm/backend/c/heaptrace.c @@ -4,6 +4,7 @@ #include "definitions.h" #include "memory.h" #include "time.h" +#include "references.h" #include #include #include @@ -363,9 +364,14 @@ FILE* teavm_gc_openDumpFile(wchar_t* name) { while (cls != NULL) { int32_t kind = (cls->flags >> 7) & 7; if (kind == 1) { - + TeaVM_Reference* reference = (TeaVM_Reference*) obj; + teavm_verify(reference->next); + teavm_verify(reference->object); + teavm_verify(reference->queue); } else if (kind == 2) { - + TeaVM_ReferenceQueue* queue = (TeaVM_ReferenceQueue*) obj; + teavm_verify(queue->first); + teavm_verify(queue->last); } else { int16_t* layout = cls->layout; if (layout != NULL) {