Skip to content

Commit 06e4cf3

Browse files
olivergillespiecoleenp
authored andcommitted
8315559: Delay TempSymbol cleanup to avoid symbol table churn
8321276: runtime/cds/appcds/dynamicArchive/DynamicSharedSymbols.java failed with "'17 2: jdk/test/lib/apps ' missing from stdout/stderr" Reviewed-by: coleenp Backport-of: d23f4f12adf1ea26b8c340efe2c3854e50b68301
1 parent 9e1840f commit 06e4cf3

File tree

5 files changed

+135
-10
lines changed

5 files changed

+135
-10
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
#include "precompiled.hpp"
25+
#include "oops/symbolHandle.hpp"
26+
#include "runtime/atomic.hpp"
27+
28+
Symbol* volatile TempSymbolCleanupDelayer::_queue[QueueSize] = {};
29+
volatile uint TempSymbolCleanupDelayer::_index = 0;
30+
31+
// Keep this symbol alive for some time to allow for reuse.
32+
// Temp symbols for the same string can often be created in quick succession,
33+
// and this queue allows them to be reused instead of churning.
34+
void TempSymbolCleanupDelayer::delay_cleanup(Symbol* sym) {
35+
assert(sym != nullptr, "precondition");
36+
sym->increment_refcount();
37+
uint i = Atomic::add(&_index, 1u) % QueueSize;
38+
Symbol* old = Atomic::xchg(&_queue[i], sym);
39+
Symbol::maybe_decrement_refcount(old);
40+
}
41+
42+
void TempSymbolCleanupDelayer::drain_queue() {
43+
for (uint i = 0; i < QueueSize; i++) {
44+
Symbol* sym = Atomic::xchg(&_queue[i], (Symbol*) nullptr);
45+
Symbol::maybe_decrement_refcount(sym);
46+
}
47+
}

src/hotspot/share/oops/symbolHandle.hpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,16 @@
2828
#include "memory/allocation.hpp"
2929
#include "oops/symbol.hpp"
3030

31+
class TempSymbolCleanupDelayer : AllStatic {
32+
static Symbol* volatile _queue[];
33+
static volatile uint _index;
34+
35+
public:
36+
static const uint QueueSize = 128;
37+
static void delay_cleanup(Symbol* s);
38+
static void drain_queue();
39+
};
40+
3141
// TempNewSymbol acts as a handle class in a handle/body idiom and is
3242
// responsible for proper resource management of the body (which is a Symbol*).
3343
// The body is resource managed by a reference counting scheme.
@@ -49,10 +59,17 @@ class SymbolHandleBase : public StackObj {
4959
SymbolHandleBase() : _temp(nullptr) { }
5060

5161
// Conversion from a Symbol* to a SymbolHandleBase.
52-
// Does not increment the current reference count if temporary.
5362
SymbolHandleBase(Symbol *s) : _temp(s) {
5463
if (!TEMP) {
5564
Symbol::maybe_increment_refcount(_temp);
65+
return;
66+
}
67+
68+
// Delay cleanup for temp symbols. Refcount is incremented while in
69+
// queue. But don't requeue existing entries, or entries that are held
70+
// elsewhere - it's a waste of effort.
71+
if (s != nullptr && s->refcount() == 1) {
72+
TempSymbolCleanupDelayer::delay_cleanup(s);
5673
}
5774
}
5875

test/hotspot/gtest/classfile/test_placeholders.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ TEST_VM(PlaceholderTable, supername) {
3939
ThreadInVMfromNative tivfn(THREAD);
4040

4141
// Assert messages assume these symbols are unique, and the refcounts start at one.
42-
TempNewSymbol A = SymbolTable::new_symbol("abc2_8_2023_class");
43-
TempNewSymbol D = SymbolTable::new_symbol("def2_8_2023_class");
42+
Symbol* A = SymbolTable::new_symbol("abc2_8_2023_class");
43+
Symbol* D = SymbolTable::new_symbol("def2_8_2023_class");
4444
Symbol* super = SymbolTable::new_symbol("super2_8_2023_supername");
45-
TempNewSymbol interf = SymbolTable::new_symbol("interface2_8_2023_supername");
45+
Symbol* interf = SymbolTable::new_symbol("interface2_8_2023_supername");
4646

4747
ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
4848

@@ -110,4 +110,9 @@ TEST_VM(PlaceholderTable, supername) {
110110
EXPECT_EQ(A->refcount(), 1) << "first lass name refcount should be 1";
111111
EXPECT_EQ(D->refcount(), 1) << "second class name refcount should be 1";
112112
EXPECT_EQ(super->refcount(), 0) << "super class name refcount should be 0 - was unloaded.";
113+
114+
// clean up temporary symbols
115+
A->decrement_refcount();
116+
D->decrement_refcount();
117+
interf->decrement_refcount();
113118
}

test/hotspot/gtest/classfile/test_symbolTable.cpp

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@
2727
#include "threadHelper.inline.hpp"
2828
#include "unittest.hpp"
2929

30+
// Helper to avoid interference from the cleanup delay queue by draining it
31+
// immediately after creation.
32+
TempNewSymbol stable_temp_symbol(Symbol* sym) {
33+
TempNewSymbol t = sym;
34+
TempSymbolCleanupDelayer::drain_queue();
35+
return t;
36+
}
37+
3038
TEST_VM(SymbolTable, temp_new_symbol) {
3139
// Assert messages assume these symbols are unique, and the refcounts start at
3240
// one, but code does not rely on this.
@@ -36,7 +44,7 @@ TEST_VM(SymbolTable, temp_new_symbol) {
3644

3745
Symbol* abc = SymbolTable::new_symbol("abc");
3846
int abccount = abc->refcount();
39-
TempNewSymbol ss = abc;
47+
TempNewSymbol ss = stable_temp_symbol(abc);
4048
ASSERT_EQ(ss->refcount(), abccount) << "only one abc";
4149
ASSERT_EQ(ss->refcount(), abc->refcount()) << "should match TempNewSymbol";
4250

@@ -45,8 +53,8 @@ TEST_VM(SymbolTable, temp_new_symbol) {
4553
int efgcount = efg->refcount();
4654
int hijcount = hij->refcount();
4755

48-
TempNewSymbol s1 = efg;
49-
TempNewSymbol s2 = hij;
56+
TempNewSymbol s1 = stable_temp_symbol(efg);
57+
TempNewSymbol s2 = stable_temp_symbol(hij);
5058
ASSERT_EQ(s1->refcount(), efgcount) << "one efg";
5159
ASSERT_EQ(s2->refcount(), hijcount) << "one hij";
5260

@@ -65,13 +73,13 @@ TEST_VM(SymbolTable, temp_new_symbol) {
6573
TempNewSymbol s3;
6674
Symbol* klm = SymbolTable::new_symbol("klm");
6775
int klmcount = klm->refcount();
68-
s3 = klm; // assignment
76+
s3 = stable_temp_symbol(klm); // assignment
6977
ASSERT_EQ(s3->refcount(), klmcount) << "only one klm now";
7078

7179
Symbol* xyz = SymbolTable::new_symbol("xyz");
7280
int xyzcount = xyz->refcount();
7381
{ // inner scope
74-
TempNewSymbol s_inner = xyz;
82+
TempNewSymbol s_inner = stable_temp_symbol(xyz);
7583
}
7684
ASSERT_EQ(xyz->refcount(), xyzcount - 1)
7785
<< "Should have been decremented by dtor in inner scope";
@@ -139,3 +147,50 @@ TEST_VM(SymbolTable, test_cleanup_leak) {
139147

140148
ASSERT_EQ(entry2->refcount(), 1) << "Symbol refcount just created is 1";
141149
}
150+
151+
TEST_VM(SymbolTable, test_cleanup_delay) {
152+
// Check that new temp symbols have an extra refcount increment, which is then
153+
// decremented when the queue spills over.
154+
155+
TempNewSymbol s1 = SymbolTable::new_symbol("temp-s1");
156+
ASSERT_EQ(s1->refcount(), 2) << "TempNewSymbol refcount just created is 2";
157+
158+
// Fill up the queue
159+
constexpr int symbol_name_length = 30;
160+
char symbol_name[symbol_name_length];
161+
for (uint i = 1; i < TempSymbolCleanupDelayer::QueueSize; i++) {
162+
os::snprintf(symbol_name, symbol_name_length, "temp-filler-%d", i);
163+
TempNewSymbol s = SymbolTable::new_symbol(symbol_name);
164+
ASSERT_EQ(s->refcount(), 2) << "TempNewSymbol refcount just created is 2";
165+
}
166+
167+
// Add one more
168+
TempNewSymbol spillover = SymbolTable::new_symbol("temp-spillover");
169+
ASSERT_EQ(spillover->refcount(), 2) << "TempNewSymbol refcount just created is 2";
170+
171+
// The first symbol should have been removed from the queue and decremented
172+
ASSERT_EQ(s1->refcount(), 1) << "TempNewSymbol off queue refcount is 1";
173+
}
174+
175+
TEST_VM(SymbolTable, test_cleanup_delay_drain) {
176+
// Fill up the queue
177+
constexpr int symbol_name_length = 30;
178+
char symbol_name[symbol_name_length];
179+
TempNewSymbol symbols[TempSymbolCleanupDelayer::QueueSize] = {};
180+
for (uint i = 0; i < TempSymbolCleanupDelayer::QueueSize; i++) {
181+
os::snprintf(symbol_name, symbol_name_length, "temp-%d", i);
182+
TempNewSymbol s = SymbolTable::new_symbol(symbol_name);
183+
symbols[i] = s;
184+
}
185+
186+
// While in the queue refcounts are incremented
187+
for (uint i = 0; i < TempSymbolCleanupDelayer::QueueSize; i++) {
188+
ASSERT_EQ(symbols[i]->refcount(), 2) << "TempNewSymbol refcount in queue is 2";
189+
}
190+
191+
// Draining the queue should decrement the refcounts
192+
TempSymbolCleanupDelayer::drain_queue();
193+
for (uint i = 0; i < TempSymbolCleanupDelayer::QueueSize; i++) {
194+
ASSERT_EQ(symbols[i]->refcount(), 1) << "TempNewSymbol refcount after drain is 1";
195+
}
196+
}

test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/DynamicSharedSymbols.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ private static void doTest(String topArchiveName) throws Exception {
9090
ProcessBuilder pb = new ProcessBuilder();
9191
pb.command(new String[] {JDKToolFinder.getJDKTool("jcmd"), Long.toString(pid), "VM.symboltable", "-verbose"});
9292
OutputAnalyzer output = CDSTestUtils.executeAndLog(pb, "jcmd-symboltable");
93-
output.shouldContain("17 2: jdk/test/lib/apps\n");
93+
output.shouldContain("17 3: jdk/test/lib/apps\n"); // 3 because a TempSymbol will be found in the TempSymbolCleanupDelayer queue.
94+
// Note: we might want to drain the queue before CDS dumps but this is correct for now, unless the queue length changes.
9495
output.shouldContain("Dynamic shared symbols:\n");
9596
output.shouldContain("5 65535: Hello\n");
9697

0 commit comments

Comments
 (0)