Skip to content

Commit 4a4c0fc

Browse files
committed
8029075: String deduplication in G1
Implementation of JEP 192, http://openjdk.java.net/jeps/192 Reviewed-by: brutisso, tschatzl, coleenp
1 parent 1e1ad7f commit 4a4c0fc

32 files changed

Lines changed: 2915 additions & 17 deletions

hotspot/make/excludeSrc.make

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ ifeq ($(INCLUDE_ALL_GCS), false)
8787
g1BlockOffsetTable.cpp g1CardCounts.cpp g1CollectedHeap.cpp g1CollectorPolicy.cpp \
8888
g1ErgoVerbose.cpp g1GCPhaseTimes.cpp g1HRPrinter.cpp g1HotCardCache.cpp g1Log.cpp \
8989
g1MMUTracker.cpp g1MarkSweep.cpp g1MemoryPool.cpp g1MonitoringSupport.cpp g1OopClosures.cpp \
90-
g1RemSet.cpp g1RemSetSummary.cpp g1SATBCardTableModRefBS.cpp g1_globals.cpp heapRegion.cpp \
90+
g1RemSet.cpp g1RemSetSummary.cpp g1SATBCardTableModRefBS.cpp g1StringDedup.cpp g1StringDedupStat.cpp \
91+
g1StringDedupTable.cpp g1StringDedupThread.cpp g1StringDedupQueue.cpp g1_globals.cpp heapRegion.cpp \
9192
g1BiasedArray.cpp heapRegionRemSet.cpp heapRegionSeq.cpp heapRegionSet.cpp heapRegionSets.cpp \
9293
ptrQueue.cpp satbQueue.cpp sparsePRT.cpp survRateGroup.cpp vm_operations_g1.cpp g1CodeCacheRemSet.cpp \
9394
adjoiningGenerations.cpp adjoiningVirtualSpaces.cpp asPSOldGen.cpp asPSYoungGen.cpp \

hotspot/src/share/vm/classfile/javaClasses.hpp

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1997, 2014, 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
@@ -61,10 +61,6 @@ class java_lang_String : AllStatic {
6161

6262
static Handle basic_create(int length, TRAPS);
6363

64-
static void set_value( oop string, typeArrayOop buffer) {
65-
assert(initialized, "Must be initialized");
66-
string->obj_field_put(value_offset, (oop)buffer);
67-
}
6864
static void set_offset(oop string, int offset) {
6965
assert(initialized, "Must be initialized");
7066
if (offset_offset > 0) {
@@ -122,12 +118,26 @@ class java_lang_String : AllStatic {
122118
return hash_offset;
123119
}
124120

121+
static void set_value(oop string, typeArrayOop buffer) {
122+
assert(initialized && (value_offset > 0), "Must be initialized");
123+
string->obj_field_put(value_offset, (oop)buffer);
124+
}
125+
static void set_hash(oop string, unsigned int hash) {
126+
assert(initialized && (hash_offset > 0), "Must be initialized");
127+
string->int_field_put(hash_offset, hash);
128+
}
129+
125130
// Accessors
126131
static typeArrayOop value(oop java_string) {
127132
assert(initialized && (value_offset > 0), "Must be initialized");
128133
assert(is_instance(java_string), "must be java_string");
129134
return (typeArrayOop) java_string->obj_field(value_offset);
130135
}
136+
static unsigned int hash(oop java_string) {
137+
assert(initialized && (hash_offset > 0), "Must be initialized");
138+
assert(is_instance(java_string), "must be java_string");
139+
return java_string->int_field(hash_offset);
140+
}
131141
static int offset(oop java_string) {
132142
assert(initialized, "Must be initialized");
133143
assert(is_instance(java_string), "must be java_string");

hotspot/src/share/vm/classfile/symbolTable.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1997, 2014, 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
@@ -35,6 +35,9 @@
3535
#include "oops/oop.inline2.hpp"
3636
#include "runtime/mutexLocker.hpp"
3737
#include "utilities/hashtable.inline.hpp"
38+
#if INCLUDE_ALL_GCS
39+
#include "gc_implementation/g1/g1StringDedup.hpp"
40+
#endif
3841

3942
// --------------------------------------------------------------------------
4043

@@ -728,6 +731,15 @@ oop StringTable::intern(Handle string_or_null, jchar* name,
728731
string = java_lang_String::create_from_unicode(name, len, CHECK_NULL);
729732
}
730733

734+
#if INCLUDE_ALL_GCS
735+
if (G1StringDedup::is_enabled()) {
736+
// Deduplicate the string before it is interned. Note that we should never
737+
// deduplicate a string after it has been interned. Doing so will counteract
738+
// compiler optimizations done on e.g. interned string literals.
739+
G1StringDedup::deduplicate(string());
740+
}
741+
#endif
742+
731743
// Grab the StringTable_lock before getting the_table() because it could
732744
// change at safepoint.
733745
MutexLocker ml(StringTable_lock, THREAD);

hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "gc_implementation/g1/g1MarkSweep.hpp"
4040
#include "gc_implementation/g1/g1OopClosures.inline.hpp"
4141
#include "gc_implementation/g1/g1RemSet.inline.hpp"
42+
#include "gc_implementation/g1/g1StringDedup.hpp"
4243
#include "gc_implementation/g1/g1YCTypes.hpp"
4344
#include "gc_implementation/g1/heapRegion.inline.hpp"
4445
#include "gc_implementation/g1/heapRegionRemSet.hpp"
@@ -2172,6 +2173,8 @@ jint G1CollectedHeap::initialize() {
21722173
// values in the heap have been properly initialized.
21732174
_g1mm = new G1MonitoringSupport(this);
21742175

2176+
G1StringDedup::initialize();
2177+
21752178
return JNI_OK;
21762179
}
21772180

@@ -3456,6 +3459,11 @@ void G1CollectedHeap::verify(bool silent, VerifyOption vo) {
34563459
if (!silent) gclog_or_tty->print("RemSet ");
34573460
rem_set()->verify();
34583461

3462+
if (G1StringDedup::is_enabled()) {
3463+
if (!silent) gclog_or_tty->print("StrDedup ");
3464+
G1StringDedup::verify();
3465+
}
3466+
34593467
if (failures) {
34603468
gclog_or_tty->print_cr("Heap:");
34613469
// It helps to have the per-region information in the output to
@@ -3473,8 +3481,13 @@ void G1CollectedHeap::verify(bool silent, VerifyOption vo) {
34733481
}
34743482
guarantee(!failures, "there should not have been any failures");
34753483
} else {
3476-
if (!silent)
3477-
gclog_or_tty->print("(SKIPPING roots, heapRegionSets, heapRegions, remset) ");
3484+
if (!silent) {
3485+
gclog_or_tty->print("(SKIPPING Roots, HeapRegionSets, HeapRegions, RemSet");
3486+
if (G1StringDedup::is_enabled()) {
3487+
gclog_or_tty->print(", StrDedup");
3488+
}
3489+
gclog_or_tty->print(") ");
3490+
}
34783491
}
34793492
}
34803493

@@ -3567,6 +3580,9 @@ void G1CollectedHeap::print_gc_threads_on(outputStream* st) const {
35673580
st->cr();
35683581
_cm->print_worker_threads_on(st);
35693582
_cg1r->print_worker_threads_on(st);
3583+
if (G1StringDedup::is_enabled()) {
3584+
G1StringDedup::print_worker_threads_on(st);
3585+
}
35703586
}
35713587

35723588
void G1CollectedHeap::gc_threads_do(ThreadClosure* tc) const {
@@ -3575,6 +3591,9 @@ void G1CollectedHeap::gc_threads_do(ThreadClosure* tc) const {
35753591
}
35763592
tc->do_thread(_cmThread);
35773593
_cg1r->threads_do(tc);
3594+
if (G1StringDedup::is_enabled()) {
3595+
G1StringDedup::threads_do(tc);
3596+
}
35783597
}
35793598

35803599
void G1CollectedHeap::print_tracing_info() const {
@@ -4755,6 +4774,13 @@ oop G1ParScanThreadState::copy_to_survivor_space(oop const old) {
47554774
obj->set_mark(m);
47564775
}
47574776

4777+
if (G1StringDedup::is_enabled()) {
4778+
G1StringDedup::enqueue_from_evacuation(from_region->is_young(),
4779+
to_region->is_young(),
4780+
queue_num(),
4781+
obj);
4782+
}
4783+
47584784
size_t* surv_young_words = surviving_young_words();
47594785
surv_young_words[young_index] += word_sz;
47604786

@@ -5218,6 +5244,10 @@ void G1CollectedHeap::unlink_string_and_symbol_table(BoolObjectClosure* is_alive
52185244
g1_unlink_task.strings_processed(), g1_unlink_task.strings_removed(),
52195245
g1_unlink_task.symbols_processed(), g1_unlink_task.symbols_removed());
52205246
}
5247+
5248+
if (G1StringDedup::is_enabled()) {
5249+
G1StringDedup::unlink(is_alive);
5250+
}
52215251
}
52225252

52235253
class RedirtyLoggedCardTableEntryFastClosure : public CardTableEntryClosure {
@@ -5841,6 +5871,9 @@ void G1CollectedHeap::evacuate_collection_set(EvacuationInfo& evacuation_info) {
58415871
G1STWIsAliveClosure is_alive(this);
58425872
G1KeepAliveClosure keep_alive(this);
58435873
JNIHandles::weak_oops_do(&is_alive, &keep_alive);
5874+
if (G1StringDedup::is_enabled()) {
5875+
G1StringDedup::unlink_or_oops_do(&is_alive, &keep_alive);
5876+
}
58445877
}
58455878

58465879
release_gc_alloc_regions(n_workers, evacuation_info);
@@ -6321,9 +6354,10 @@ void G1CollectedHeap::tear_down_region_sets(bool free_list_only) {
63216354
TearDownRegionSetsClosure cl(&_old_set);
63226355
heap_region_iterate(&cl);
63236356

6324-
// Need to do this after the heap iteration to be able to
6325-
// recognize the young regions and ignore them during the iteration.
6326-
_young_list->empty_list();
6357+
// Note that emptying the _young_list is postponed and instead done as
6358+
// the first step when rebuilding the regions sets again. The reason for
6359+
// this is that during a full GC string deduplication needs to know if
6360+
// a collected region was young or old when the full GC was initiated.
63276361
}
63286362
_free_list.remove_all();
63296363
}
@@ -6377,6 +6411,10 @@ class RebuildRegionSetsClosure : public HeapRegionClosure {
63776411
void G1CollectedHeap::rebuild_region_sets(bool free_list_only) {
63786412
assert_at_safepoint(true /* should_be_vm_thread */);
63796413

6414+
if (!free_list_only) {
6415+
_young_list->empty_list();
6416+
}
6417+
63806418
RebuildRegionSetsClosure cl(free_list_only, &_old_set, &_free_list);
63816419
heap_region_iterate(&cl);
63826420

hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "gc_implementation/g1/g1CollectedHeap.inline.hpp"
2828
#include "gc_implementation/g1/g1GCPhaseTimes.hpp"
2929
#include "gc_implementation/g1/g1Log.hpp"
30+
#include "gc_implementation/g1/g1StringDedup.hpp"
3031

3132
// Helper class for avoiding interleaved logging
3233
class LineBuffer: public StackObj {
@@ -168,7 +169,9 @@ G1GCPhaseTimes::G1GCPhaseTimes(uint max_gc_threads) :
168169
_last_termination_attempts(_max_gc_threads, SIZE_FORMAT),
169170
_last_gc_worker_end_times_ms(_max_gc_threads, "%.1lf", false),
170171
_last_gc_worker_times_ms(_max_gc_threads, "%.1lf"),
171-
_last_gc_worker_other_times_ms(_max_gc_threads, "%.1lf")
172+
_last_gc_worker_other_times_ms(_max_gc_threads, "%.1lf"),
173+
_cur_string_dedup_queue_fixup_worker_times_ms(_max_gc_threads, "%.1lf"),
174+
_cur_string_dedup_table_fixup_worker_times_ms(_max_gc_threads, "%.1lf")
172175
{
173176
assert(max_gc_threads > 0, "Must have some GC threads");
174177
}
@@ -229,6 +232,16 @@ void G1GCPhaseTimes::note_gc_end() {
229232
_last_gc_worker_other_times_ms.verify();
230233
}
231234

235+
void G1GCPhaseTimes::note_string_dedup_fixup_start() {
236+
_cur_string_dedup_queue_fixup_worker_times_ms.reset();
237+
_cur_string_dedup_table_fixup_worker_times_ms.reset();
238+
}
239+
240+
void G1GCPhaseTimes::note_string_dedup_fixup_end() {
241+
_cur_string_dedup_queue_fixup_worker_times_ms.verify();
242+
_cur_string_dedup_table_fixup_worker_times_ms.verify();
243+
}
244+
232245
void G1GCPhaseTimes::print_stats(int level, const char* str, double value) {
233246
LineBuffer(level).append_and_print_cr("[%s: %.1lf ms]", str, value);
234247
}
@@ -253,6 +266,11 @@ double G1GCPhaseTimes::accounted_time_ms() {
253266
// Strong code root purge time
254267
misc_time_ms += _cur_strong_code_root_purge_time_ms;
255268

269+
if (G1StringDedup::is_enabled()) {
270+
// String dedup fixup time
271+
misc_time_ms += _cur_string_dedup_fixup_time_ms;
272+
}
273+
256274
// Subtract the time taken to clean the card table from the
257275
// current value of "other time"
258276
misc_time_ms += _cur_clear_ct_time_ms;
@@ -303,6 +321,11 @@ void G1GCPhaseTimes::print(double pause_time_sec) {
303321
print_stats(1, "Code Root Fixup", _cur_collection_code_root_fixup_time_ms);
304322
print_stats(1, "Code Root Migration", _cur_strong_code_root_migration_time_ms);
305323
print_stats(1, "Code Root Purge", _cur_strong_code_root_purge_time_ms);
324+
if (G1StringDedup::is_enabled()) {
325+
print_stats(1, "String Dedup Fixup", _cur_string_dedup_fixup_time_ms, _active_gc_threads);
326+
_cur_string_dedup_queue_fixup_worker_times_ms.print(2, "Queue Fixup (ms)");
327+
_cur_string_dedup_table_fixup_worker_times_ms.print(2, "Table Fixup (ms)");
328+
}
306329
print_stats(1, "Clear CT", _cur_clear_ct_time_ms);
307330
double misc_time_ms = pause_time_sec * MILLIUNITS - accounted_time_ms();
308331
print_stats(1, "Other", misc_time_ms);

hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.hpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,10 @@ class G1GCPhaseTimes : public CHeapObj<mtGC> {
137137
double _cur_evac_fail_restore_remsets;
138138
double _cur_evac_fail_remove_self_forwards;
139139

140+
double _cur_string_dedup_fixup_time_ms;
141+
WorkerDataArray<double> _cur_string_dedup_queue_fixup_worker_times_ms;
142+
WorkerDataArray<double> _cur_string_dedup_table_fixup_worker_times_ms;
143+
140144
double _cur_clear_ct_time_ms;
141145
double _cur_ref_proc_time_ms;
142146
double _cur_ref_enq_time_ms;
@@ -246,6 +250,21 @@ class G1GCPhaseTimes : public CHeapObj<mtGC> {
246250
_cur_evac_fail_remove_self_forwards = ms;
247251
}
248252

253+
void note_string_dedup_fixup_start();
254+
void note_string_dedup_fixup_end();
255+
256+
void record_string_dedup_fixup_time(double ms) {
257+
_cur_string_dedup_fixup_time_ms = ms;
258+
}
259+
260+
void record_string_dedup_queue_fixup_worker_time(uint worker_id, double ms) {
261+
_cur_string_dedup_queue_fixup_worker_times_ms.set(worker_id, ms);
262+
}
263+
264+
void record_string_dedup_table_fixup_worker_time(uint worker_id, double ms) {
265+
_cur_string_dedup_table_fixup_worker_times_ms.set(worker_id, ms);
266+
}
267+
249268
void record_ref_proc_time(double ms) {
250269
_cur_ref_proc_time_ms = ms;
251270
}

hotspot/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2001, 2014, 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
@@ -31,6 +31,7 @@
3131
#include "code/icBuffer.hpp"
3232
#include "gc_implementation/g1/g1Log.hpp"
3333
#include "gc_implementation/g1/g1MarkSweep.hpp"
34+
#include "gc_implementation/g1/g1StringDedup.hpp"
3435
#include "gc_implementation/shared/gcHeapSummary.hpp"
3536
#include "gc_implementation/shared/gcTimer.hpp"
3637
#include "gc_implementation/shared/gcTrace.hpp"
@@ -316,6 +317,10 @@ void G1MarkSweep::mark_sweep_phase3() {
316317
// have been cleared if they pointed to non-surviving objects.)
317318
sh->process_weak_roots(&GenMarkSweep::adjust_pointer_closure);
318319

320+
if (G1StringDedup::is_enabled()) {
321+
G1StringDedup::oops_do(&GenMarkSweep::adjust_pointer_closure);
322+
}
323+
319324
GenMarkSweep::adjust_marks();
320325

321326
G1AdjustPointersClosure blk;

0 commit comments

Comments
 (0)