Skip to content

Commit 79f1dfb

Browse files
committed
8255987: JDI tests fail with com.sun.jdi.ObjectCollectedException
Reviewed-by: dholmes, cjplummer
1 parent 9ce3d80 commit 79f1dfb

File tree

8 files changed

+168
-33
lines changed

8 files changed

+168
-33
lines changed

src/jdk.jdwp.agent/share/native/libjdwp/commonRef.c

Lines changed: 118 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -87,38 +87,49 @@ static RefNode *
8787
createNode(JNIEnv *env, jobject ref)
8888
{
8989
RefNode *node;
90-
jobject weakRef;
90+
jobject strongOrWeakRef;
9191
jvmtiError error;
92+
jboolean pin = gdata->pinAllCount != 0;
9293

9394
/* Could allocate RefNode's in blocks, not sure it would help much */
9495
node = (RefNode*)jvmtiAllocate((int)sizeof(RefNode));
9596
if (node == NULL) {
9697
return NULL;
9798
}
9899

99-
/* Create weak reference to make sure we have a reference */
100-
weakRef = JNI_FUNC_PTR(env,NewWeakGlobalRef)(env, ref);
101-
// NewWeakGlobalRef can throw OOM, clear exception here.
102-
if ((*env)->ExceptionCheck(env)) {
103-
(*env)->ExceptionClear(env);
104-
jvmtiDeallocate(node);
105-
return NULL;
100+
if (pin) {
101+
/* Create strong reference to make sure we have a reference */
102+
strongOrWeakRef = JNI_FUNC_PTR(env,NewGlobalRef)(env, ref);
103+
} else {
104+
/* Create weak reference to make sure we have a reference */
105+
strongOrWeakRef = JNI_FUNC_PTR(env,NewWeakGlobalRef)(env, ref);
106+
107+
// NewWeakGlobalRef can throw OOM, clear exception here.
108+
if ((*env)->ExceptionCheck(env)) {
109+
(*env)->ExceptionClear(env);
110+
jvmtiDeallocate(node);
111+
return NULL;
112+
}
106113
}
107114

108-
/* Set tag on weakRef */
115+
/* Set tag on strongOrWeakRef */
109116
error = JVMTI_FUNC_PTR(gdata->jvmti, SetTag)
110-
(gdata->jvmti, weakRef, ptr_to_jlong(node));
117+
(gdata->jvmti, strongOrWeakRef, ptr_to_jlong(node));
111118
if ( error != JVMTI_ERROR_NONE ) {
112-
JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, weakRef);
119+
if (pin) {
120+
JNI_FUNC_PTR(env,DeleteGlobalRef)(env, strongOrWeakRef);
121+
} else {
122+
JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, strongOrWeakRef);
123+
}
113124
jvmtiDeallocate(node);
114125
return NULL;
115126
}
116127

117128
/* Fill in RefNode */
118-
node->ref = weakRef;
119-
node->isStrong = JNI_FALSE;
120-
node->count = 1;
121-
node->seqNum = newSeqNum();
129+
node->ref = strongOrWeakRef;
130+
node->count = 1;
131+
node->strongCount = pin ? 1 : 0;
132+
node->seqNum = newSeqNum();
122133

123134
/* Count RefNode's created */
124135
gdata->objectsByIDcount++;
@@ -135,7 +146,7 @@ deleteNode(JNIEnv *env, RefNode *node)
135146
/* Clear tag */
136147
(void)JVMTI_FUNC_PTR(gdata->jvmti,SetTag)
137148
(gdata->jvmti, node->ref, NULL_OBJECT_ID);
138-
if (node->isStrong) {
149+
if (node->strongCount != 0) {
139150
JNI_FUNC_PTR(env,DeleteGlobalRef)(env, node->ref);
140151
} else {
141152
JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, node->ref);
@@ -149,7 +160,7 @@ deleteNode(JNIEnv *env, RefNode *node)
149160
static jobject
150161
strengthenNode(JNIEnv *env, RefNode *node)
151162
{
152-
if (!node->isStrong) {
163+
if (node->strongCount == 0) {
153164
jobject strongRef;
154165

155166
strongRef = JNI_FUNC_PTR(env,NewGlobalRef)(env, node->ref);
@@ -164,11 +175,12 @@ strengthenNode(JNIEnv *env, RefNode *node)
164175
}
165176
if (strongRef != NULL) {
166177
JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, node->ref);
167-
node->ref = strongRef;
168-
node->isStrong = JNI_TRUE;
178+
node->ref = strongRef;
179+
node->strongCount = 1;
169180
}
170181
return strongRef;
171182
} else {
183+
node->strongCount++;
172184
return node->ref;
173185
}
174186
}
@@ -177,7 +189,7 @@ strengthenNode(JNIEnv *env, RefNode *node)
177189
static jweak
178190
weakenNode(JNIEnv *env, RefNode *node)
179191
{
180-
if (node->isStrong) {
192+
if (node->strongCount == 1) {
181193
jweak weakRef;
182194

183195
weakRef = JNI_FUNC_PTR(env,NewWeakGlobalRef)(env, node->ref);
@@ -188,11 +200,12 @@ weakenNode(JNIEnv *env, RefNode *node)
188200

189201
if (weakRef != NULL) {
190202
JNI_FUNC_PTR(env,DeleteGlobalRef)(env, node->ref);
191-
node->ref = weakRef;
192-
node->isStrong = JNI_FALSE;
203+
node->ref = weakRef;
204+
node->strongCount = 0;
193205
}
194206
return weakRef;
195207
} else {
208+
node->strongCount--;
196209
return node->ref;
197210
}
198211
}
@@ -372,7 +385,8 @@ void
372385
commonRef_initialize(void)
373386
{
374387
gdata->refLock = debugMonitorCreate("JDWP Reference Table Monitor");
375-
gdata->nextSeqNum = 1; /* 0 used for error indication */
388+
gdata->nextSeqNum = 1; /* 0 used for error indication */
389+
gdata->pinAllCount = 0;
376390
initializeObjectsByID(HASH_INIT_SIZE);
377391
}
378392

@@ -454,7 +468,7 @@ commonRef_idToRef(JNIEnv *env, jlong id)
454468

455469
node = findNodeByID(env, id);
456470
if (node != NULL) {
457-
if (node->isStrong) {
471+
if (node->strongCount != 0) {
458472
saveGlobalRef(env, node->ref, &ref);
459473
} else {
460474
jobject lref;
@@ -544,6 +558,84 @@ commonRef_unpin(jlong id)
544558
return error;
545559
}
546560

561+
/* Prevent garbage collection of object */
562+
void
563+
commonRef_pinAll()
564+
{
565+
debugMonitorEnter(gdata->refLock); {
566+
gdata->pinAllCount++;
567+
568+
if (gdata->pinAllCount == 1) {
569+
JNIEnv *env;
570+
RefNode *node;
571+
RefNode *prev;
572+
int i;
573+
574+
env = getEnv();
575+
576+
/*
577+
* Walk through the id-based hash table. Detach any nodes
578+
* for which the ref has been collected.
579+
*/
580+
for (i = 0; i < gdata->objectsByIDsize; i++) {
581+
node = gdata->objectsByID[i];
582+
prev = NULL;
583+
while (node != NULL) {
584+
jobject strongRef;
585+
586+
strongRef = strengthenNode(env, node);
587+
588+
/* Has the object been collected? */
589+
if (strongRef == NULL) {
590+
RefNode *freed;
591+
592+
/* Detach from the ID list */
593+
if (prev == NULL) {
594+
gdata->objectsByID[i] = node->next;
595+
} else {
596+
prev->next = node->next;
597+
}
598+
freed = node;
599+
node = node->next;
600+
deleteNode(env, freed);
601+
} else {
602+
prev = node;
603+
node = node->next;
604+
}
605+
}
606+
}
607+
}
608+
} debugMonitorExit(gdata->refLock);
609+
}
610+
611+
/* Permit garbage collection of objects */
612+
void
613+
commonRef_unpinAll()
614+
{
615+
debugMonitorEnter(gdata->refLock); {
616+
gdata->pinAllCount--;
617+
618+
if (gdata->pinAllCount == 0) {
619+
JNIEnv *env;
620+
RefNode *node;
621+
int i;
622+
623+
env = getEnv();
624+
625+
for (i = 0; i < gdata->objectsByIDsize; i++) {
626+
for (node = gdata->objectsByID[i]; node != NULL; node = node->next) {
627+
jweak weakRef;
628+
629+
weakRef = weakenNode(env, node);
630+
if (weakRef == NULL) {
631+
EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"NewWeakGlobalRef");
632+
}
633+
}
634+
}
635+
}
636+
} debugMonitorExit(gdata->refLock);
637+
}
638+
547639
/* Release tracking of an object by ID */
548640
void
549641
commonRef_release(JNIEnv *env, jlong id)
@@ -582,7 +674,7 @@ commonRef_compact(void)
582674
prev = NULL;
583675
while (node != NULL) {
584676
/* Has the object been collected? */
585-
if ( (!node->isStrong) &&
677+
if ( (node->strongCount == 0) &&
586678
isSameObject(env, node->ref, NULL)) {
587679
RefNode *freed;
588680

src/jdk.jdwp.agent/share/native/libjdwp/commonRef.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1998, 2005, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -34,6 +34,8 @@ jobject commonRef_idToRef(JNIEnv *env, jlong id);
3434
void commonRef_idToRef_delete(JNIEnv *env, jobject ref);
3535
jvmtiError commonRef_pin(jlong id);
3636
jvmtiError commonRef_unpin(jlong id);
37+
void commonRef_pinAll();
38+
void commonRef_unpinAll();
3739
void commonRef_releaseMultiple(JNIEnv *env, jlong id, jint refCount);
3840
void commonRef_release(JNIEnv *env, jlong id);
3941
void commonRef_compact(void);

src/jdk.jdwp.agent/share/native/libjdwp/threadControl.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1553,6 +1553,12 @@ threadControl_suspendAll(void)
15531553
}
15541554

15551555
if (error == JVMTI_ERROR_NONE) {
1556+
/*
1557+
* Pin all objects to prevent objects from being
1558+
* garbage collected while the VM is suspended.
1559+
*/
1560+
commonRef_pinAll();
1561+
15561562
suspendAllCount++;
15571563
}
15581564

@@ -1604,6 +1610,11 @@ threadControl_resumeAll(void)
16041610
}
16051611

16061612
if (suspendAllCount > 0) {
1613+
/*
1614+
* Unpin all objects.
1615+
*/
1616+
commonRef_unpinAll();
1617+
16071618
suspendAllCount--;
16081619
}
16091620

src/jdk.jdwp.agent/share/native/libjdwp/util.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ typedef struct RefNode {
6565
jobject ref; /* could be strong or weak */
6666
struct RefNode *next; /* next RefNode* in bucket chain */
6767
jint count; /* count of references */
68-
unsigned isStrong : 1; /* 1 means this is a string reference */
68+
unsigned strongCount; /* count of strong reference */
6969
} RefNode;
7070

7171
/* Value of a NULL ID */
@@ -128,6 +128,7 @@ typedef struct {
128128
/* Common References static data */
129129
jrawMonitorID refLock;
130130
jlong nextSeqNum;
131+
unsigned pinAllCount;
131132
RefNode **objectsByID;
132133
int objectsByIDsize;
133134
int objectsByIDcount;

test/hotspot/jtreg/vmTestbase/nsk/jdi/ArrayType/newInstance/newinstance004.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -144,9 +144,23 @@ private int runThis (String argv[], PrintStream out) {
144144
log1(" TESTING BEGINS");
145145

146146
for (int i = 0; ; i++) {
147-
pipe.println("newcheck");
147+
pipe.println("newcheck");
148+
149+
// There are potentially other non-test Java threads allocating objects and triggering
150+
// GC's so we need to suspend the target VM to avoid the objects created in the test
151+
// from being accidentally GC'ed. However, we need the target VM temporary resumed
152+
// while reading its response. Below we resume the target VM (if required) and suspend
153+
// it only after pipe.readln() returns.
154+
155+
// On the first iteration the target VM is not suspended yet.
156+
if (i > 0) {
157+
debuggee.resume();
158+
}
148159
line = pipe.readln();
149160

161+
// Suspending target VM to prevent other non-test Java threads from triggering GCs.
162+
debuggee.suspend();
163+
150164
if (line.equals("checkend")) {
151165
log2(" : returned string is 'checkend'");
152166
break ;
@@ -230,6 +244,7 @@ private int runThis (String argv[], PrintStream out) {
230244
//-------------------------------------------------- test summary section
231245
//------------------------------------------------- standard end section
232246

247+
debuggee.resume();
233248
pipe.println("quit");
234249
log2("waiting for the debuggee to finish ...");
235250
debuggee.waitFor();

test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/instances/instances002/instances002.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,9 @@ public void testClassType(String className) {
189189
objectReferences.add(classType.newInstance(breakpointEvent.thread(), method, new ArrayList<Value>(), 0));
190190
}
191191

192+
debuggee.resume();
192193
checkDebugeeAnswer_instances(className, baseInstances);
194+
debuggee.suspend();
193195

194196
break;
195197
}

test/hotspot/jtreg/vmTestbase/nsk/jdi/VMOutOfMemoryException/VMOutOfMemoryException001/VMOutOfMemoryException001.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,14 @@ public void doTest() {
7979
// create array in debuggee VM till VMOutOfMemoryException
8080
while (true) {
8181
ArrayReference array = referenceType.newInstance(100000);
82-
array.disableCollection();
82+
try {
83+
// Since the VM is not suspended, the object may have been collected
84+
// before disableCollection() could be called on it. Just ignore and
85+
// continue doing allocations until we run out of memory.
86+
array.disableCollection();
87+
} catch (ObjectCollectedException e) {
88+
continue;
89+
}
8390
objects.add(array);
8491
}
8592
} catch (VMOutOfMemoryException e) {

test/hotspot/jtreg/vmTestbase/nsk/share/jdi/sde/SDEDebuggee.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -67,10 +67,15 @@ public boolean parseCommand(String command) {
6767
return false;
6868
}
6969

70+
// Keep class loader alive to avoid ObjectCollectedException
71+
// on the debugger side, in case the GC unloads the class and
72+
// invalidates code locations.
73+
private TestClassLoader classLoader;
74+
7075
// create instance of given class and execute all methods which names start
7176
// with 'sde_testMethod'
7277
private void executeTestMethods(String className) {
73-
TestClassLoader classLoader = new TestClassLoader();
78+
classLoader = new TestClassLoader();
7479
classLoader.setClassPath(classpath);
7580

7681
try {

0 commit comments

Comments
 (0)