8029075: String deduplication in G1

Tue, 18 Mar 2014 19:07:22 +0100

author
pliden
date
Tue, 18 Mar 2014 19:07:22 +0100
changeset 6413
595c0f60d50d
parent 6412
6e7e363c5a8f
child 6415
a007d73f3fdc
child 6423
2775f322649a

8029075: String deduplication in G1
Summary: Implementation of JEP 192, http://openjdk.java.net/jeps/192
Reviewed-by: brutisso, tschatzl, coleenp

make/excludeSrc.make file | annotate | diff | comparison | revisions
src/share/vm/classfile/javaClasses.hpp file | annotate | diff | comparison | revisions
src/share/vm/classfile/symbolTable.cpp file | annotate | diff | comparison | revisions
src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp file | annotate | diff | comparison | revisions
src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp file | annotate | diff | comparison | revisions
src/share/vm/gc_implementation/g1/g1GCPhaseTimes.hpp file | annotate | diff | comparison | revisions
src/share/vm/gc_implementation/g1/g1MarkSweep.cpp file | annotate | diff | comparison | revisions
src/share/vm/gc_implementation/g1/g1StringDedup.cpp file | annotate | diff | comparison | revisions
src/share/vm/gc_implementation/g1/g1StringDedup.hpp file | annotate | diff | comparison | revisions
src/share/vm/gc_implementation/g1/g1StringDedupQueue.cpp file | annotate | diff | comparison | revisions
src/share/vm/gc_implementation/g1/g1StringDedupQueue.hpp file | annotate | diff | comparison | revisions
src/share/vm/gc_implementation/g1/g1StringDedupStat.cpp file | annotate | diff | comparison | revisions
src/share/vm/gc_implementation/g1/g1StringDedupStat.hpp file | annotate | diff | comparison | revisions
src/share/vm/gc_implementation/g1/g1StringDedupTable.cpp file | annotate | diff | comparison | revisions
src/share/vm/gc_implementation/g1/g1StringDedupTable.hpp file | annotate | diff | comparison | revisions
src/share/vm/gc_implementation/g1/g1StringDedupThread.cpp file | annotate | diff | comparison | revisions
src/share/vm/gc_implementation/g1/g1StringDedupThread.hpp file | annotate | diff | comparison | revisions
src/share/vm/gc_implementation/shared/markSweep.inline.hpp file | annotate | diff | comparison | revisions
src/share/vm/runtime/arguments.cpp file | annotate | diff | comparison | revisions
src/share/vm/runtime/globals.hpp file | annotate | diff | comparison | revisions
src/share/vm/runtime/mutexLocker.cpp file | annotate | diff | comparison | revisions
src/share/vm/runtime/mutexLocker.hpp file | annotate | diff | comparison | revisions
test/gc/g1/TestGCLogMessages.java file | annotate | diff | comparison | revisions
test/gc/g1/TestStringDeduplicationAgeThreshold.java file | annotate | diff | comparison | revisions
test/gc/g1/TestStringDeduplicationFullGC.java file | annotate | diff | comparison | revisions
test/gc/g1/TestStringDeduplicationInterned.java file | annotate | diff | comparison | revisions
test/gc/g1/TestStringDeduplicationMemoryUsage.java file | annotate | diff | comparison | revisions
test/gc/g1/TestStringDeduplicationPrintOptions.java file | annotate | diff | comparison | revisions
test/gc/g1/TestStringDeduplicationTableRehash.java file | annotate | diff | comparison | revisions
test/gc/g1/TestStringDeduplicationTableResize.java file | annotate | diff | comparison | revisions
test/gc/g1/TestStringDeduplicationTools.java file | annotate | diff | comparison | revisions
test/gc/g1/TestStringDeduplicationYoungGC.java file | annotate | diff | comparison | revisions
     1.1 --- a/make/excludeSrc.make	Mon Mar 24 09:14:14 2014 -0700
     1.2 +++ b/make/excludeSrc.make	Tue Mar 18 19:07:22 2014 +0100
     1.3 @@ -87,7 +87,8 @@
     1.4  	g1BlockOffsetTable.cpp g1CardCounts.cpp g1CollectedHeap.cpp g1CollectorPolicy.cpp \
     1.5  	g1ErgoVerbose.cpp g1GCPhaseTimes.cpp g1HRPrinter.cpp g1HotCardCache.cpp g1Log.cpp \
     1.6  	g1MMUTracker.cpp g1MarkSweep.cpp g1MemoryPool.cpp g1MonitoringSupport.cpp g1OopClosures.cpp \
     1.7 -	g1RemSet.cpp g1RemSetSummary.cpp g1SATBCardTableModRefBS.cpp g1_globals.cpp heapRegion.cpp \
     1.8 +	g1RemSet.cpp g1RemSetSummary.cpp g1SATBCardTableModRefBS.cpp g1StringDedup.cpp g1StringDedupStat.cpp \
     1.9 +	g1StringDedupTable.cpp g1StringDedupThread.cpp g1StringDedupQueue.cpp g1_globals.cpp heapRegion.cpp \
    1.10  	g1BiasedArray.cpp heapRegionRemSet.cpp heapRegionSeq.cpp heapRegionSet.cpp heapRegionSets.cpp \
    1.11  	ptrQueue.cpp satbQueue.cpp sparsePRT.cpp survRateGroup.cpp vm_operations_g1.cpp g1CodeCacheRemSet.cpp \
    1.12  	adjoiningGenerations.cpp adjoiningVirtualSpaces.cpp asPSOldGen.cpp asPSYoungGen.cpp \
     2.1 --- a/src/share/vm/classfile/javaClasses.hpp	Mon Mar 24 09:14:14 2014 -0700
     2.2 +++ b/src/share/vm/classfile/javaClasses.hpp	Tue Mar 18 19:07:22 2014 +0100
     2.3 @@ -1,5 +1,5 @@
     2.4  /*
     2.5 - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
     2.6 + * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
     2.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     2.8   *
     2.9   * This code is free software; you can redistribute it and/or modify it
    2.10 @@ -61,10 +61,6 @@
    2.11  
    2.12    static Handle basic_create(int length, TRAPS);
    2.13  
    2.14 -  static void set_value( oop string, typeArrayOop buffer) {
    2.15 -    assert(initialized, "Must be initialized");
    2.16 -    string->obj_field_put(value_offset,  (oop)buffer);
    2.17 -  }
    2.18    static void set_offset(oop string, int offset) {
    2.19      assert(initialized, "Must be initialized");
    2.20      if (offset_offset > 0) {
    2.21 @@ -122,12 +118,26 @@
    2.22      return hash_offset;
    2.23    }
    2.24  
    2.25 +  static void set_value(oop string, typeArrayOop buffer) {
    2.26 +    assert(initialized && (value_offset > 0), "Must be initialized");
    2.27 +    string->obj_field_put(value_offset, (oop)buffer);
    2.28 +  }
    2.29 +  static void set_hash(oop string, unsigned int hash) {
    2.30 +    assert(initialized && (hash_offset > 0), "Must be initialized");
    2.31 +    string->int_field_put(hash_offset, hash);
    2.32 +  }
    2.33 +
    2.34    // Accessors
    2.35    static typeArrayOop value(oop java_string) {
    2.36      assert(initialized && (value_offset > 0), "Must be initialized");
    2.37      assert(is_instance(java_string), "must be java_string");
    2.38      return (typeArrayOop) java_string->obj_field(value_offset);
    2.39    }
    2.40 +  static unsigned int hash(oop java_string) {
    2.41 +    assert(initialized && (hash_offset > 0), "Must be initialized");
    2.42 +    assert(is_instance(java_string), "must be java_string");
    2.43 +    return java_string->int_field(hash_offset);
    2.44 +  }
    2.45    static int offset(oop java_string) {
    2.46      assert(initialized, "Must be initialized");
    2.47      assert(is_instance(java_string), "must be java_string");
     3.1 --- a/src/share/vm/classfile/symbolTable.cpp	Mon Mar 24 09:14:14 2014 -0700
     3.2 +++ b/src/share/vm/classfile/symbolTable.cpp	Tue Mar 18 19:07:22 2014 +0100
     3.3 @@ -1,5 +1,5 @@
     3.4  /*
     3.5 - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
     3.6 + * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
     3.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3.8   *
     3.9   * This code is free software; you can redistribute it and/or modify it
    3.10 @@ -35,6 +35,9 @@
    3.11  #include "oops/oop.inline2.hpp"
    3.12  #include "runtime/mutexLocker.hpp"
    3.13  #include "utilities/hashtable.inline.hpp"
    3.14 +#if INCLUDE_ALL_GCS
    3.15 +#include "gc_implementation/g1/g1StringDedup.hpp"
    3.16 +#endif
    3.17  
    3.18  // --------------------------------------------------------------------------
    3.19  
    3.20 @@ -728,6 +731,15 @@
    3.21      string = java_lang_String::create_from_unicode(name, len, CHECK_NULL);
    3.22    }
    3.23  
    3.24 +#if INCLUDE_ALL_GCS
    3.25 +  if (G1StringDedup::is_enabled()) {
    3.26 +    // Deduplicate the string before it is interned. Note that we should never
    3.27 +    // deduplicate a string after it has been interned. Doing so will counteract
    3.28 +    // compiler optimizations done on e.g. interned string literals.
    3.29 +    G1StringDedup::deduplicate(string());
    3.30 +  }
    3.31 +#endif
    3.32 +
    3.33    // Grab the StringTable_lock before getting the_table() because it could
    3.34    // change at safepoint.
    3.35    MutexLocker ml(StringTable_lock, THREAD);
     4.1 --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp	Mon Mar 24 09:14:14 2014 -0700
     4.2 +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp	Tue Mar 18 19:07:22 2014 +0100
     4.3 @@ -39,6 +39,7 @@
     4.4  #include "gc_implementation/g1/g1MarkSweep.hpp"
     4.5  #include "gc_implementation/g1/g1OopClosures.inline.hpp"
     4.6  #include "gc_implementation/g1/g1RemSet.inline.hpp"
     4.7 +#include "gc_implementation/g1/g1StringDedup.hpp"
     4.8  #include "gc_implementation/g1/g1YCTypes.hpp"
     4.9  #include "gc_implementation/g1/heapRegion.inline.hpp"
    4.10  #include "gc_implementation/g1/heapRegionRemSet.hpp"
    4.11 @@ -2173,6 +2174,8 @@
    4.12    // values in the heap have been properly initialized.
    4.13    _g1mm = new G1MonitoringSupport(this);
    4.14  
    4.15 +  G1StringDedup::initialize();
    4.16 +
    4.17    return JNI_OK;
    4.18  }
    4.19  
    4.20 @@ -3475,6 +3478,11 @@
    4.21      if (!silent) gclog_or_tty->print("RemSet ");
    4.22      rem_set()->verify();
    4.23  
    4.24 +    if (G1StringDedup::is_enabled()) {
    4.25 +      if (!silent) gclog_or_tty->print("StrDedup ");
    4.26 +      G1StringDedup::verify();
    4.27 +    }
    4.28 +
    4.29      if (failures) {
    4.30        gclog_or_tty->print_cr("Heap:");
    4.31        // It helps to have the per-region information in the output to
    4.32 @@ -3492,8 +3500,13 @@
    4.33      }
    4.34      guarantee(!failures, "there should not have been any failures");
    4.35    } else {
    4.36 -    if (!silent)
    4.37 -      gclog_or_tty->print("(SKIPPING roots, heapRegionSets, heapRegions, remset) ");
    4.38 +    if (!silent) {
    4.39 +      gclog_or_tty->print("(SKIPPING Roots, HeapRegionSets, HeapRegions, RemSet");
    4.40 +      if (G1StringDedup::is_enabled()) {
    4.41 +        gclog_or_tty->print(", StrDedup");
    4.42 +      }
    4.43 +      gclog_or_tty->print(") ");
    4.44 +    }
    4.45    }
    4.46  }
    4.47  
    4.48 @@ -3586,6 +3599,9 @@
    4.49    st->cr();
    4.50    _cm->print_worker_threads_on(st);
    4.51    _cg1r->print_worker_threads_on(st);
    4.52 +  if (G1StringDedup::is_enabled()) {
    4.53 +    G1StringDedup::print_worker_threads_on(st);
    4.54 +  }
    4.55  }
    4.56  
    4.57  void G1CollectedHeap::gc_threads_do(ThreadClosure* tc) const {
    4.58 @@ -3594,6 +3610,9 @@
    4.59    }
    4.60    tc->do_thread(_cmThread);
    4.61    _cg1r->threads_do(tc);
    4.62 +  if (G1StringDedup::is_enabled()) {
    4.63 +    G1StringDedup::threads_do(tc);
    4.64 +  }
    4.65  }
    4.66  
    4.67  void G1CollectedHeap::print_tracing_info() const {
    4.68 @@ -4774,6 +4793,13 @@
    4.69        obj->set_mark(m);
    4.70      }
    4.71  
    4.72 +    if (G1StringDedup::is_enabled()) {
    4.73 +      G1StringDedup::enqueue_from_evacuation(from_region->is_young(),
    4.74 +                                             to_region->is_young(),
    4.75 +                                             queue_num(),
    4.76 +                                             obj);
    4.77 +    }
    4.78 +
    4.79      size_t* surv_young_words = surviving_young_words();
    4.80      surv_young_words[young_index] += word_sz;
    4.81  
    4.82 @@ -5249,6 +5275,10 @@
    4.83                             g1_unlink_task.strings_processed(), g1_unlink_task.strings_removed(),
    4.84                             g1_unlink_task.symbols_processed(), g1_unlink_task.symbols_removed());
    4.85    }
    4.86 +
    4.87 +  if (G1StringDedup::is_enabled()) {
    4.88 +    G1StringDedup::unlink(is_alive);
    4.89 +  }
    4.90  }
    4.91  
    4.92  class RedirtyLoggedCardTableEntryFastClosure : public CardTableEntryClosure {
    4.93 @@ -5872,6 +5902,9 @@
    4.94      G1STWIsAliveClosure is_alive(this);
    4.95      G1KeepAliveClosure keep_alive(this);
    4.96      JNIHandles::weak_oops_do(&is_alive, &keep_alive);
    4.97 +    if (G1StringDedup::is_enabled()) {
    4.98 +      G1StringDedup::unlink_or_oops_do(&is_alive, &keep_alive);
    4.99 +    }
   4.100    }
   4.101  
   4.102    release_gc_alloc_regions(n_workers, evacuation_info);
   4.103 @@ -6352,9 +6385,10 @@
   4.104      TearDownRegionSetsClosure cl(&_old_set);
   4.105      heap_region_iterate(&cl);
   4.106  
   4.107 -    // Need to do this after the heap iteration to be able to
   4.108 -    // recognize the young regions and ignore them during the iteration.
   4.109 -    _young_list->empty_list();
   4.110 +    // Note that emptying the _young_list is postponed and instead done as
   4.111 +    // the first step when rebuilding the regions sets again. The reason for
   4.112 +    // this is that during a full GC string deduplication needs to know if
   4.113 +    // a collected region was young or old when the full GC was initiated.
   4.114    }
   4.115    _free_list.remove_all();
   4.116  }
   4.117 @@ -6408,6 +6442,10 @@
   4.118  void G1CollectedHeap::rebuild_region_sets(bool free_list_only) {
   4.119    assert_at_safepoint(true /* should_be_vm_thread */);
   4.120  
   4.121 +  if (!free_list_only) {
   4.122 +    _young_list->empty_list();
   4.123 +  }
   4.124 +
   4.125    RebuildRegionSetsClosure cl(free_list_only, &_old_set, &_free_list);
   4.126    heap_region_iterate(&cl);
   4.127  
     5.1 --- a/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp	Mon Mar 24 09:14:14 2014 -0700
     5.2 +++ b/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp	Tue Mar 18 19:07:22 2014 +0100
     5.3 @@ -27,6 +27,7 @@
     5.4  #include "gc_implementation/g1/g1CollectedHeap.inline.hpp"
     5.5  #include "gc_implementation/g1/g1GCPhaseTimes.hpp"
     5.6  #include "gc_implementation/g1/g1Log.hpp"
     5.7 +#include "gc_implementation/g1/g1StringDedup.hpp"
     5.8  
     5.9  // Helper class for avoiding interleaved logging
    5.10  class LineBuffer: public StackObj {
    5.11 @@ -168,7 +169,9 @@
    5.12    _last_termination_attempts(_max_gc_threads, SIZE_FORMAT),
    5.13    _last_gc_worker_end_times_ms(_max_gc_threads, "%.1lf", false),
    5.14    _last_gc_worker_times_ms(_max_gc_threads, "%.1lf"),
    5.15 -  _last_gc_worker_other_times_ms(_max_gc_threads, "%.1lf")
    5.16 +  _last_gc_worker_other_times_ms(_max_gc_threads, "%.1lf"),
    5.17 +  _cur_string_dedup_queue_fixup_worker_times_ms(_max_gc_threads, "%.1lf"),
    5.18 +  _cur_string_dedup_table_fixup_worker_times_ms(_max_gc_threads, "%.1lf")
    5.19  {
    5.20    assert(max_gc_threads > 0, "Must have some GC threads");
    5.21  }
    5.22 @@ -229,6 +232,16 @@
    5.23    _last_gc_worker_other_times_ms.verify();
    5.24  }
    5.25  
    5.26 +void G1GCPhaseTimes::note_string_dedup_fixup_start() {
    5.27 +  _cur_string_dedup_queue_fixup_worker_times_ms.reset();
    5.28 +  _cur_string_dedup_table_fixup_worker_times_ms.reset();
    5.29 +}
    5.30 +
    5.31 +void G1GCPhaseTimes::note_string_dedup_fixup_end() {
    5.32 +  _cur_string_dedup_queue_fixup_worker_times_ms.verify();
    5.33 +  _cur_string_dedup_table_fixup_worker_times_ms.verify();
    5.34 +}
    5.35 +
    5.36  void G1GCPhaseTimes::print_stats(int level, const char* str, double value) {
    5.37    LineBuffer(level).append_and_print_cr("[%s: %.1lf ms]", str, value);
    5.38  }
    5.39 @@ -253,6 +266,11 @@
    5.40      // Strong code root purge time
    5.41      misc_time_ms += _cur_strong_code_root_purge_time_ms;
    5.42  
    5.43 +    if (G1StringDedup::is_enabled()) {
    5.44 +      // String dedup fixup time
    5.45 +      misc_time_ms += _cur_string_dedup_fixup_time_ms;
    5.46 +    }
    5.47 +
    5.48      // Subtract the time taken to clean the card table from the
    5.49      // current value of "other time"
    5.50      misc_time_ms += _cur_clear_ct_time_ms;
    5.51 @@ -303,6 +321,11 @@
    5.52    print_stats(1, "Code Root Fixup", _cur_collection_code_root_fixup_time_ms);
    5.53    print_stats(1, "Code Root Migration", _cur_strong_code_root_migration_time_ms);
    5.54    print_stats(1, "Code Root Purge", _cur_strong_code_root_purge_time_ms);
    5.55 +  if (G1StringDedup::is_enabled()) {
    5.56 +    print_stats(1, "String Dedup Fixup", _cur_string_dedup_fixup_time_ms, _active_gc_threads);
    5.57 +    _cur_string_dedup_queue_fixup_worker_times_ms.print(2, "Queue Fixup (ms)");
    5.58 +    _cur_string_dedup_table_fixup_worker_times_ms.print(2, "Table Fixup (ms)");
    5.59 +  }
    5.60    print_stats(1, "Clear CT", _cur_clear_ct_time_ms);
    5.61    double misc_time_ms = pause_time_sec * MILLIUNITS - accounted_time_ms();
    5.62    print_stats(1, "Other", misc_time_ms);
     6.1 --- a/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.hpp	Mon Mar 24 09:14:14 2014 -0700
     6.2 +++ b/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.hpp	Tue Mar 18 19:07:22 2014 +0100
     6.3 @@ -137,6 +137,10 @@
     6.4    double _cur_evac_fail_restore_remsets;
     6.5    double _cur_evac_fail_remove_self_forwards;
     6.6  
     6.7 +  double                  _cur_string_dedup_fixup_time_ms;
     6.8 +  WorkerDataArray<double> _cur_string_dedup_queue_fixup_worker_times_ms;
     6.9 +  WorkerDataArray<double> _cur_string_dedup_table_fixup_worker_times_ms;
    6.10 +
    6.11    double _cur_clear_ct_time_ms;
    6.12    double _cur_ref_proc_time_ms;
    6.13    double _cur_ref_enq_time_ms;
    6.14 @@ -246,6 +250,21 @@
    6.15      _cur_evac_fail_remove_self_forwards = ms;
    6.16    }
    6.17  
    6.18 +  void note_string_dedup_fixup_start();
    6.19 +  void note_string_dedup_fixup_end();
    6.20 +
    6.21 +  void record_string_dedup_fixup_time(double ms) {
    6.22 +    _cur_string_dedup_fixup_time_ms = ms;
    6.23 +  }
    6.24 +
    6.25 +  void record_string_dedup_queue_fixup_worker_time(uint worker_id, double ms) {
    6.26 +    _cur_string_dedup_queue_fixup_worker_times_ms.set(worker_id, ms);
    6.27 +  }
    6.28 +
    6.29 +  void record_string_dedup_table_fixup_worker_time(uint worker_id, double ms) {
    6.30 +    _cur_string_dedup_table_fixup_worker_times_ms.set(worker_id, ms);
    6.31 +  }
    6.32 +
    6.33    void record_ref_proc_time(double ms) {
    6.34      _cur_ref_proc_time_ms = ms;
    6.35    }
     7.1 --- a/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp	Mon Mar 24 09:14:14 2014 -0700
     7.2 +++ b/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp	Tue Mar 18 19:07:22 2014 +0100
     7.3 @@ -1,5 +1,5 @@
     7.4  /*
     7.5 - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved.
     7.6 + * Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved.
     7.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     7.8   *
     7.9   * This code is free software; you can redistribute it and/or modify it
    7.10 @@ -31,6 +31,7 @@
    7.11  #include "code/icBuffer.hpp"
    7.12  #include "gc_implementation/g1/g1Log.hpp"
    7.13  #include "gc_implementation/g1/g1MarkSweep.hpp"
    7.14 +#include "gc_implementation/g1/g1StringDedup.hpp"
    7.15  #include "gc_implementation/shared/gcHeapSummary.hpp"
    7.16  #include "gc_implementation/shared/gcTimer.hpp"
    7.17  #include "gc_implementation/shared/gcTrace.hpp"
    7.18 @@ -320,6 +321,10 @@
    7.19    // have been cleared if they pointed to non-surviving objects.)
    7.20    g1h->g1_process_weak_roots(&GenMarkSweep::adjust_pointer_closure);
    7.21  
    7.22 +  if (G1StringDedup::is_enabled()) {
    7.23 +    G1StringDedup::oops_do(&GenMarkSweep::adjust_pointer_closure);
    7.24 +  }
    7.25 +
    7.26    GenMarkSweep::adjust_marks();
    7.27  
    7.28    G1AdjustPointersClosure blk;
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/src/share/vm/gc_implementation/g1/g1StringDedup.cpp	Tue Mar 18 19:07:22 2014 +0100
     8.3 @@ -0,0 +1,208 @@
     8.4 +/*
     8.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
     8.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     8.7 + *
     8.8 + * This code is free software; you can redistribute it and/or modify it
     8.9 + * under the terms of the GNU General Public License version 2 only, as
    8.10 + * published by the Free Software Foundation.
    8.11 + *
    8.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
    8.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    8.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    8.15 + * version 2 for more details (a copy is included in the LICENSE file that
    8.16 + * accompanied this code).
    8.17 + *
    8.18 + * You should have received a copy of the GNU General Public License version
    8.19 + * 2 along with this work; if not, write to the Free Software Foundation,
    8.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    8.21 + *
    8.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    8.23 + * or visit www.oracle.com if you need additional information or have any
    8.24 + * questions.
    8.25 + *
    8.26 + */
    8.27 +
    8.28 +#include "precompiled.hpp"
    8.29 +#include "classfile/javaClasses.hpp"
    8.30 +#include "gc_implementation/g1/g1CollectedHeap.inline.hpp"
    8.31 +#include "gc_implementation/g1/g1GCPhaseTimes.hpp"
    8.32 +#include "gc_implementation/g1/g1StringDedup.hpp"
    8.33 +#include "gc_implementation/g1/g1StringDedupQueue.hpp"
    8.34 +#include "gc_implementation/g1/g1StringDedupStat.hpp"
    8.35 +#include "gc_implementation/g1/g1StringDedupTable.hpp"
    8.36 +#include "gc_implementation/g1/g1StringDedupThread.hpp"
    8.37 +
    8.38 +bool G1StringDedup::_enabled = false;
    8.39 +
    8.40 +void G1StringDedup::initialize() {
    8.41 +  assert(UseG1GC, "String deduplication only available with G1");
    8.42 +  if (UseStringDeduplication) {
    8.43 +    _enabled = true;
    8.44 +    G1StringDedupQueue::create();
    8.45 +    G1StringDedupTable::create();
    8.46 +    G1StringDedupThread::create();
    8.47 +  }
    8.48 +}
    8.49 +
    8.50 +bool G1StringDedup::is_candidate_from_mark(oop obj) {
    8.51 +  if (java_lang_String::is_instance(obj)) {
    8.52 +    bool from_young = G1CollectedHeap::heap()->heap_region_containing_raw(obj)->is_young();
    8.53 +    if (from_young && obj->age() < StringDeduplicationAgeThreshold) {
    8.54 +      // Candidate found. String is being evacuated from young to old but has not
    8.55 +      // reached the deduplication age threshold, i.e. has not previously been a
    8.56 +      // candidate during its life in the young generation.
    8.57 +      return true;
    8.58 +    }
    8.59 +  }
    8.60 +
    8.61 +  // Not a candidate
    8.62 +  return false;
    8.63 +}
    8.64 +
    8.65 +void G1StringDedup::enqueue_from_mark(oop java_string) {
    8.66 +  assert(is_enabled(), "String deduplication not enabled");
    8.67 +  if (is_candidate_from_mark(java_string)) {
    8.68 +    G1StringDedupQueue::push(0 /* worker_id */, java_string);
    8.69 +  }
    8.70 +}
    8.71 +
    8.72 +bool G1StringDedup::is_candidate_from_evacuation(bool from_young, bool to_young, oop obj) {
    8.73 +  if (from_young && java_lang_String::is_instance(obj)) {
    8.74 +    if (to_young && obj->age() == StringDeduplicationAgeThreshold) {
    8.75 +      // Candidate found. String is being evacuated from young to young and just
    8.76 +      // reached the deduplication age threshold.
    8.77 +      return true;
    8.78 +    }
    8.79 +    if (!to_young && obj->age() < StringDeduplicationAgeThreshold) {
    8.80 +      // Candidate found. String is being evacuated from young to old but has not
    8.81 +      // reached the deduplication age threshold, i.e. has not previously been a
    8.82 +      // candidate during its life in the young generation.
    8.83 +      return true;
    8.84 +    }
    8.85 +  }
    8.86 +
    8.87 +  // Not a candidate
    8.88 +  return false;
    8.89 +}
    8.90 +
    8.91 +void G1StringDedup::enqueue_from_evacuation(bool from_young, bool to_young, uint worker_id, oop java_string) {
    8.92 +  assert(is_enabled(), "String deduplication not enabled");
    8.93 +  if (is_candidate_from_evacuation(from_young, to_young, java_string)) {
    8.94 +    G1StringDedupQueue::push(worker_id, java_string);
    8.95 +  }
    8.96 +}
    8.97 +
    8.98 +void G1StringDedup::deduplicate(oop java_string) {
    8.99 +  assert(is_enabled(), "String deduplication not enabled");
   8.100 +  G1StringDedupStat dummy; // Statistics from this path is never used
   8.101 +  G1StringDedupTable::deduplicate(java_string, dummy);
   8.102 +}
   8.103 +
   8.104 +void G1StringDedup::oops_do(OopClosure* keep_alive) {
   8.105 +  assert(is_enabled(), "String deduplication not enabled");
   8.106 +  unlink_or_oops_do(NULL, keep_alive);
   8.107 +}
   8.108 +
   8.109 +void G1StringDedup::unlink(BoolObjectClosure* is_alive) {
   8.110 +  assert(is_enabled(), "String deduplication not enabled");
   8.111 +  // Don't allow a potential resize or rehash during unlink, as the unlink
   8.112 +  // operation itself might remove enough entries to invalidate such a decision.
   8.113 +  unlink_or_oops_do(is_alive, NULL, false /* allow_resize_and_rehash */);
   8.114 +}
   8.115 +
   8.116 +//
   8.117 +// Task for parallel unlink_or_oops_do() operation on the deduplication queue
   8.118 +// and table.
   8.119 +//
   8.120 +class G1StringDedupUnlinkOrOopsDoTask : public AbstractGangTask {
   8.121 +private:
   8.122 +  G1StringDedupUnlinkOrOopsDoClosure _cl;
   8.123 +
   8.124 +public:
   8.125 +  G1StringDedupUnlinkOrOopsDoTask(BoolObjectClosure* is_alive,
   8.126 +                                  OopClosure* keep_alive,
   8.127 +                                  bool allow_resize_and_rehash) :
   8.128 +    AbstractGangTask("G1StringDedupUnlinkOrOopsDoTask"),
   8.129 +    _cl(is_alive, keep_alive, allow_resize_and_rehash) {
   8.130 +  }
   8.131 +
   8.132 +  virtual void work(uint worker_id) {
   8.133 +    double queue_fixup_start = os::elapsedTime();
   8.134 +    G1StringDedupQueue::unlink_or_oops_do(&_cl);
   8.135 +
   8.136 +    double table_fixup_start = os::elapsedTime();
   8.137 +    G1StringDedupTable::unlink_or_oops_do(&_cl, worker_id);
   8.138 +
   8.139 +    double queue_fixup_time_ms = (table_fixup_start - queue_fixup_start) * 1000.0;
   8.140 +    double table_fixup_time_ms = (os::elapsedTime() - table_fixup_start) * 1000.0;
   8.141 +    G1CollectorPolicy* g1p = G1CollectedHeap::heap()->g1_policy();
   8.142 +    g1p->phase_times()->record_string_dedup_queue_fixup_worker_time(worker_id, queue_fixup_time_ms);
   8.143 +    g1p->phase_times()->record_string_dedup_table_fixup_worker_time(worker_id, table_fixup_time_ms);
   8.144 +  }
   8.145 +};
   8.146 +
   8.147 +void G1StringDedup::unlink_or_oops_do(BoolObjectClosure* is_alive, OopClosure* keep_alive, bool allow_resize_and_rehash) {
   8.148 +  assert(is_enabled(), "String deduplication not enabled");
   8.149 +  G1CollectorPolicy* g1p = G1CollectedHeap::heap()->g1_policy();
   8.150 +  g1p->phase_times()->note_string_dedup_fixup_start();
   8.151 +  double fixup_start = os::elapsedTime();
   8.152 +
   8.153 +  G1StringDedupUnlinkOrOopsDoTask task(is_alive, keep_alive, allow_resize_and_rehash);
   8.154 +  if (G1CollectedHeap::use_parallel_gc_threads()) {
   8.155 +    G1CollectedHeap* g1h = G1CollectedHeap::heap();
   8.156 +    g1h->set_par_threads();
   8.157 +    g1h->workers()->run_task(&task);
   8.158 +    g1h->set_par_threads(0);
   8.159 +  } else {
   8.160 +    task.work(0);
   8.161 +  }
   8.162 +
   8.163 +  double fixup_time_ms = (os::elapsedTime() - fixup_start) * 1000.0;
   8.164 +  g1p->phase_times()->record_string_dedup_fixup_time(fixup_time_ms);
   8.165 +  g1p->phase_times()->note_string_dedup_fixup_end();
   8.166 +}
   8.167 +
   8.168 +void G1StringDedup::threads_do(ThreadClosure* tc) {
   8.169 +  assert(is_enabled(), "String deduplication not enabled");
   8.170 +  tc->do_thread(G1StringDedupThread::thread());
   8.171 +}
   8.172 +
   8.173 +void G1StringDedup::print_worker_threads_on(outputStream* st) {
   8.174 +  assert(is_enabled(), "String deduplication not enabled");
   8.175 +  G1StringDedupThread::thread()->print_on(st);
   8.176 +  st->cr();
   8.177 +}
   8.178 +
   8.179 +void G1StringDedup::verify() {
   8.180 +  assert(is_enabled(), "String deduplication not enabled");
   8.181 +  G1StringDedupQueue::verify();
   8.182 +  G1StringDedupTable::verify();
   8.183 +}
   8.184 +
   8.185 +G1StringDedupUnlinkOrOopsDoClosure::G1StringDedupUnlinkOrOopsDoClosure(BoolObjectClosure* is_alive,
   8.186 +                                                                       OopClosure* keep_alive,
   8.187 +                                                                       bool allow_resize_and_rehash) :
   8.188 +  _is_alive(is_alive),
   8.189 +  _keep_alive(keep_alive),
   8.190 +  _resized_table(NULL),
   8.191 +  _rehashed_table(NULL),
   8.192 +  _next_queue(0),
   8.193 +  _next_bucket(0) {
   8.194 +  if (allow_resize_and_rehash) {
   8.195 +    // If both resize and rehash is needed, only do resize. Rehash of
   8.196 +    // the table will eventually happen if the situation persists.
   8.197 +    _resized_table = G1StringDedupTable::prepare_resize();
   8.198 +    if (!is_resizing()) {
   8.199 +      _rehashed_table = G1StringDedupTable::prepare_rehash();
   8.200 +    }
   8.201 +  }
   8.202 +}
   8.203 +
   8.204 +G1StringDedupUnlinkOrOopsDoClosure::~G1StringDedupUnlinkOrOopsDoClosure() {
   8.205 +  assert(!is_resizing() || !is_rehashing(), "Can not both resize and rehash");
   8.206 +  if (is_resizing()) {
   8.207 +    G1StringDedupTable::finish_resize(_resized_table);
   8.208 +  } else if (is_rehashing()) {
   8.209 +    G1StringDedupTable::finish_rehash(_rehashed_table);
   8.210 +  }
   8.211 +}
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/src/share/vm/gc_implementation/g1/g1StringDedup.hpp	Tue Mar 18 19:07:22 2014 +0100
     9.3 @@ -0,0 +1,202 @@
     9.4 +/*
     9.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
     9.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     9.7 + *
     9.8 + * This code is free software; you can redistribute it and/or modify it
     9.9 + * under the terms of the GNU General Public License version 2 only, as
    9.10 + * published by the Free Software Foundation.
    9.11 + *
    9.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
    9.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    9.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    9.15 + * version 2 for more details (a copy is included in the LICENSE file that
    9.16 + * accompanied this code).
    9.17 + *
    9.18 + * You should have received a copy of the GNU General Public License version
    9.19 + * 2 along with this work; if not, write to the Free Software Foundation,
    9.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    9.21 + *
    9.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    9.23 + * or visit www.oracle.com if you need additional information or have any
    9.24 + * questions.
    9.25 + *
    9.26 + */
    9.27 +
    9.28 +#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUP_HPP
    9.29 +#define SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUP_HPP
    9.30 +
    9.31 +//
    9.32 +// String Deduplication
    9.33 +//
    9.34 +// String deduplication aims to reduce the heap live-set by deduplicating identical
    9.35 +// instances of String so that they share the same backing character array.
    9.36 +//
    9.37 +// The deduplication process is divided in two main parts, 1) finding the objects to
    9.38 +// deduplicate, and 2) deduplicating those objects. The first part is done as part of
    9.39 +// a normal GC cycle when objects are marked or evacuated. At this time a check is
    9.40 +// applied on each object to check if it is a candidate for deduplication. If so, the
    9.41 +// object is placed on the deduplication queue for later processing. The second part,
    9.42 +// processing the objects on the deduplication queue, is a concurrent phase which
    9.43 +// starts right after the stop-the-wold marking/evacuation phase. This phase is
    9.44 +// executed by the deduplication thread, which pulls deduplication candidates of the
    9.45 +// deduplication queue and tries to deduplicate them.
    9.46 +//
    9.47 +// A deduplication hashtable is used to keep track of all unique character arrays
    9.48 +// used by String objects. When deduplicating, a lookup is made in this table to see
    9.49 +// if there is already an identical character array somewhere on the heap. If so, the
    9.50 +// String object is adjusted to point to that character array, releasing the reference
    9.51 +// to the original array allowing it to eventually be garbage collected. If the lookup
    9.52 +// fails the character array is instead inserted into the hashtable so that this array
    9.53 +// can be shared at some point in the future.
    9.54 +//
    9.55 +// Candidate selection
    9.56 +//
    9.57 +// An object is considered a deduplication candidate if all of the following
    9.58 +// statements are true:
    9.59 +//
    9.60 +// - The object is an instance of java.lang.String
    9.61 +//
    9.62 +// - The object is being evacuated from a young heap region
    9.63 +//
    9.64 +// - The object is being evacuated to a young/survivor heap region and the
    9.65 +//   object's age is equal to the deduplication age threshold
    9.66 +//
    9.67 +//   or
    9.68 +//
    9.69 +//   The object is being evacuated to an old heap region and the object's age is
    9.70 +//   less than the deduplication age threshold
    9.71 +//
    9.72 +// Once an string object has been promoted to an old region, or its age is higher
    9.73 +// than the deduplication age threshold, is will never become a candidate again.
    9.74 +// This approach avoids making the same object a candidate more than once.
    9.75 +//
    9.76 +// Interned strings are a bit special. They are explicitly deduplicated just before
    9.77 +// being inserted into the StringTable (to avoid counteracting C2 optimizations done
    9.78 +// on string literals), then they also become deduplication candidates if they reach
    9.79 +// the deduplication age threshold or are evacuated to an old heap region. The second
    9.80 +// attempt to deduplicate such strings will be in vain, but we have no fast way of
    9.81 +// filtering them out. This has not shown to be a problem, as the number of interned
    9.82 +// strings is usually dwarfed by the number of normal (non-interned) strings.
    9.83 +//
    9.84 +// For additional information on string deduplication, please see JEP 192,
    9.85 +// http://openjdk.java.net/jeps/192
    9.86 +//
    9.87 +
    9.88 +#include "memory/allocation.hpp"
    9.89 +#include "oops/oop.hpp"
    9.90 +
    9.91 +class OopClosure;
    9.92 +class BoolObjectClosure;
    9.93 +class ThreadClosure;
    9.94 +class outputStream;
    9.95 +class G1StringDedupTable;
    9.96 +
    9.97 +//
    9.98 +// Main interface for interacting with string deduplication.
    9.99 +//
   9.100 +class G1StringDedup : public AllStatic {
   9.101 +private:
   9.102 +  // Single state for checking if both G1 and string deduplication is enabled.
   9.103 +  static bool _enabled;
   9.104 +
   9.105 +  // Candidate selection policies, returns true if the given object is
   9.106 +  // candidate for string deduplication.
   9.107 +  static bool is_candidate_from_mark(oop obj);
   9.108 +  static bool is_candidate_from_evacuation(bool from_young, bool to_young, oop obj);
   9.109 +
   9.110 +public:
   9.111 +  // Returns true if both G1 and string deduplication is enabled.
   9.112 +  static bool is_enabled() {
   9.113 +    return _enabled;
   9.114 +  }
   9.115 +
   9.116 +  static void initialize();
   9.117 +
   9.118 +  // Immediately deduplicates the given String object, bypassing the
   9.119 +  // the deduplication queue.
   9.120 +  static void deduplicate(oop java_string);
   9.121 +
   9.122 +  // Enqueues a deduplication candidate for later processing by the deduplication
   9.123 +  // thread. Before enqueuing, these functions apply the appropriate candidate
   9.124 +  // selection policy to filters out non-candidates.
   9.125 +  static void enqueue_from_mark(oop java_string);
   9.126 +  static void enqueue_from_evacuation(bool from_young, bool to_young,
   9.127 +                                      unsigned int queue, oop java_string);
   9.128 +
   9.129 +  static void oops_do(OopClosure* keep_alive);
   9.130 +  static void unlink(BoolObjectClosure* is_alive);
   9.131 +  static void unlink_or_oops_do(BoolObjectClosure* is_alive, OopClosure* keep_alive,
   9.132 +                                bool allow_resize_and_rehash = true);
   9.133 +
   9.134 +  static void threads_do(ThreadClosure* tc);
   9.135 +  static void print_worker_threads_on(outputStream* st);
   9.136 +  static void verify();
   9.137 +};
   9.138 +
   9.139 +//
   9.140 +// This closure encapsulates the state and the closures needed when scanning
   9.141 +// the deduplication queue and table during the unlink_or_oops_do() operation.
   9.142 +// A single instance of this closure is created and then shared by all worker
   9.143 +// threads participating in the scan. The _next_queue and _next_bucket fields
   9.144 +// provide a simple mechanism for GC workers to claim exclusive access to a
   9.145 +// queue or a table partition.
   9.146 +//
   9.147 +class G1StringDedupUnlinkOrOopsDoClosure : public StackObj {
   9.148 +private:
   9.149 +  BoolObjectClosure*  _is_alive;
   9.150 +  OopClosure*         _keep_alive;
   9.151 +  G1StringDedupTable* _resized_table;
   9.152 +  G1StringDedupTable* _rehashed_table;
   9.153 +  size_t              _next_queue;
   9.154 +  size_t              _next_bucket;
   9.155 +
   9.156 +public:
   9.157 +  G1StringDedupUnlinkOrOopsDoClosure(BoolObjectClosure* is_alive,
   9.158 +                                     OopClosure* keep_alive,
   9.159 +                                     bool allow_resize_and_rehash);
   9.160 +  ~G1StringDedupUnlinkOrOopsDoClosure();
   9.161 +
   9.162 +  bool is_resizing() {
   9.163 +    return _resized_table != NULL;
   9.164 +  }
   9.165 +
   9.166 +  G1StringDedupTable* resized_table() {
   9.167 +    return _resized_table;
   9.168 +  }
   9.169 +
   9.170 +  bool is_rehashing() {
   9.171 +    return _rehashed_table != NULL;
   9.172 +  }
   9.173 +
   9.174 +  // Atomically claims the next available queue for exclusive access by
   9.175 +  // the current thread. Returns the queue number of the claimed queue.
   9.176 +  size_t claim_queue() {
   9.177 +    return (size_t)Atomic::add_ptr(1, &_next_queue) - 1;
   9.178 +  }
   9.179 +
   9.180 +  // Atomically claims the next available table partition for exclusive
   9.181 +  // access by the current thread. Returns the table bucket number where
   9.182 +  // the claimed partition starts.
   9.183 +  size_t claim_table_partition(size_t partition_size) {
   9.184 +    return (size_t)Atomic::add_ptr(partition_size, &_next_bucket) - partition_size;
   9.185 +  }
   9.186 +
   9.187 +  // Applies and returns the result from the is_alive closure, or
   9.188 +  // returns true if no such closure was provided.
   9.189 +  bool is_alive(oop o) {
   9.190 +    if (_is_alive != NULL) {
   9.191 +      return _is_alive->do_object_b(o);
   9.192 +    }
   9.193 +    return true;
   9.194 +  }
   9.195 +
   9.196 +  // Applies the keep_alive closure, or does nothing if no such
   9.197 +  // closure was provided.
   9.198 +  void keep_alive(oop* p) {
   9.199 +    if (_keep_alive != NULL) {
   9.200 +      _keep_alive->do_oop(p);
   9.201 +    }
   9.202 +  }
   9.203 +};
   9.204 +
   9.205 +#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUP_HPP
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/src/share/vm/gc_implementation/g1/g1StringDedupQueue.cpp	Tue Mar 18 19:07:22 2014 +0100
    10.3 @@ -0,0 +1,162 @@
    10.4 +/*
    10.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
    10.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    10.7 + *
    10.8 + * This code is free software; you can redistribute it and/or modify it
    10.9 + * under the terms of the GNU General Public License version 2 only, as
   10.10 + * published by the Free Software Foundation.
   10.11 + *
   10.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
   10.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   10.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   10.15 + * version 2 for more details (a copy is included in the LICENSE file that
   10.16 + * accompanied this code).
   10.17 + *
   10.18 + * You should have received a copy of the GNU General Public License version
   10.19 + * 2 along with this work; if not, write to the Free Software Foundation,
   10.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   10.21 + *
   10.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   10.23 + * or visit www.oracle.com if you need additional information or have any
   10.24 + * questions.
   10.25 + *
   10.26 + */
   10.27 +
   10.28 +#include "precompiled.hpp"
   10.29 +#include "classfile/javaClasses.hpp"
   10.30 +#include "gc_implementation/g1/g1StringDedupQueue.hpp"
   10.31 +#include "memory/gcLocker.hpp"
   10.32 +#include "runtime/mutexLocker.hpp"
   10.33 +#include "utilities/stack.inline.hpp"
   10.34 +
   10.35 +G1StringDedupQueue* G1StringDedupQueue::_queue = NULL;
   10.36 +const size_t        G1StringDedupQueue::_max_size = 1000000; // Max number of elements per queue
   10.37 +const size_t        G1StringDedupQueue::_max_cache_size = 0; // Max cache size per queue
   10.38 +
   10.39 +G1StringDedupQueue::G1StringDedupQueue() :
   10.40 +  _cursor(0),
   10.41 +  _empty(true),
   10.42 +  _dropped(0) {
   10.43 +  _nqueues = MAX2(ParallelGCThreads, (size_t)1);
   10.44 +  _queues = NEW_C_HEAP_ARRAY(G1StringDedupWorkerQueue, _nqueues, mtGC);
   10.45 +  for (size_t i = 0; i < _nqueues; i++) {
   10.46 +    new (_queues + i) G1StringDedupWorkerQueue(G1StringDedupWorkerQueue::default_segment_size(), _max_cache_size, _max_size);
   10.47 +  }
   10.48 +}
   10.49 +
   10.50 +G1StringDedupQueue::~G1StringDedupQueue() {
   10.51 +  ShouldNotReachHere();
   10.52 +}
   10.53 +
   10.54 +void G1StringDedupQueue::create() {
   10.55 +  assert(_queue == NULL, "One string deduplication queue allowed");
   10.56 +  _queue = new G1StringDedupQueue();
   10.57 +}
   10.58 +
   10.59 +void G1StringDedupQueue::wait() {
   10.60 +  MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
   10.61 +  while (_queue->_empty) {
   10.62 +    ml.wait(Mutex::_no_safepoint_check_flag);
   10.63 +  }
   10.64 +}
   10.65 +
   10.66 +void G1StringDedupQueue::push(uint worker_id, oop java_string) {
   10.67 +  assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint");
   10.68 +  assert(worker_id < _queue->_nqueues, "Invalid queue");
   10.69 +
   10.70 +  // Push and notify waiter
   10.71 +  G1StringDedupWorkerQueue& worker_queue = _queue->_queues[worker_id];
   10.72 +  if (!worker_queue.is_full()) {
   10.73 +    worker_queue.push(java_string);
   10.74 +    if (_queue->_empty) {
   10.75 +      MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
   10.76 +      if (_queue->_empty) {
   10.77 +        // Mark non-empty and notify waiter
   10.78 +        _queue->_empty = false;
   10.79 +        ml.notify();
   10.80 +      }
   10.81 +    }
   10.82 +  } else {
   10.83 +    // Queue is full, drop the string and update the statistics
   10.84 +    Atomic::inc_ptr(&_queue->_dropped);
   10.85 +  }
   10.86 +}
   10.87 +
   10.88 +oop G1StringDedupQueue::pop() {
   10.89 +  assert(!SafepointSynchronize::is_at_safepoint(), "Must not be at safepoint");
   10.90 +  No_Safepoint_Verifier nsv;
   10.91 +
   10.92 +  // Try all queues before giving up
   10.93 +  for (size_t tries = 0; tries < _queue->_nqueues; tries++) {
   10.94 +    // The cursor indicates where we left of last time
   10.95 +    G1StringDedupWorkerQueue* queue = &_queue->_queues[_queue->_cursor];
   10.96 +    while (!queue->is_empty()) {
   10.97 +      oop obj = queue->pop();
   10.98 +      // The oop we pop can be NULL if it was marked
   10.99 +      // dead. Just ignore those and pop the next oop.
  10.100 +      if (obj != NULL) {
  10.101 +        return obj;
  10.102 +      }
  10.103 +    }
  10.104 +
  10.105 +    // Try next queue
  10.106 +    _queue->_cursor = (_queue->_cursor + 1) % _queue->_nqueues;
  10.107 +  }
  10.108 +
  10.109 +  // Mark empty
  10.110 +  _queue->_empty = true;
  10.111 +
  10.112 +  return NULL;
  10.113 +}
  10.114 +
  10.115 +void G1StringDedupQueue::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl) {
  10.116 +  // A worker thread first claims a queue, which ensures exclusive
  10.117 +  // access to that queue, then continues to process it.
  10.118 +  for (;;) {
  10.119 +    // Grab next queue to scan
  10.120 +    size_t queue = cl->claim_queue();
  10.121 +    if (queue >= _queue->_nqueues) {
  10.122 +      // End of queues
  10.123 +      break;
  10.124 +    }
  10.125 +
  10.126 +    // Scan the queue
  10.127 +    unlink_or_oops_do(cl, queue);
  10.128 +  }
  10.129 +}
  10.130 +
  10.131 +void G1StringDedupQueue::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl, size_t queue) {
  10.132 +  assert(queue < _queue->_nqueues, "Invalid queue");
  10.133 +  StackIterator<oop, mtGC> iter(_queue->_queues[queue]);
  10.134 +  while (!iter.is_empty()) {
  10.135 +    oop* p = iter.next_addr();
  10.136 +    if (*p != NULL) {
  10.137 +      if (cl->is_alive(*p)) {
  10.138 +        cl->keep_alive(p);
  10.139 +      } else {
  10.140 +        // Clear dead reference
  10.141 +        *p = NULL;
  10.142 +      }
  10.143 +    }
  10.144 +  }
  10.145 +}
  10.146 +
  10.147 +void G1StringDedupQueue::print_statistics(outputStream* st) {
  10.148 +  st->print_cr(
  10.149 +    "   [Queue]\n"
  10.150 +    "      [Dropped: "UINTX_FORMAT"]", _queue->_dropped);
  10.151 +}
  10.152 +
  10.153 +void G1StringDedupQueue::verify() {
  10.154 +  for (size_t i = 0; i < _queue->_nqueues; i++) {
  10.155 +    StackIterator<oop, mtGC> iter(_queue->_queues[i]);
  10.156 +    while (!iter.is_empty()) {
  10.157 +      oop obj = iter.next();
  10.158 +      if (obj != NULL) {
  10.159 +        guarantee(Universe::heap()->is_in_reserved(obj), "Object must be on the heap");
  10.160 +        guarantee(!obj->is_forwarded(), "Object must not be forwarded");
  10.161 +        guarantee(java_lang_String::is_instance(obj), "Object must be a String");
  10.162 +      }
  10.163 +    }
  10.164 +  }
  10.165 +}
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/src/share/vm/gc_implementation/g1/g1StringDedupQueue.hpp	Tue Mar 18 19:07:22 2014 +0100
    11.3 @@ -0,0 +1,97 @@
    11.4 +/*
    11.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
    11.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    11.7 + *
    11.8 + * This code is free software; you can redistribute it and/or modify it
    11.9 + * under the terms of the GNU General Public License version 2 only, as
   11.10 + * published by the Free Software Foundation.
   11.11 + *
   11.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
   11.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   11.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   11.15 + * version 2 for more details (a copy is included in the LICENSE file that
   11.16 + * accompanied this code).
   11.17 + *
   11.18 + * You should have received a copy of the GNU General Public License version
   11.19 + * 2 along with this work; if not, write to the Free Software Foundation,
   11.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   11.21 + *
   11.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   11.23 + * or visit www.oracle.com if you need additional information or have any
   11.24 + * questions.
   11.25 + *
   11.26 + */
   11.27 +
   11.28 +#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPQUEUE_HPP
   11.29 +#define SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPQUEUE_HPP
   11.30 +
   11.31 +#include "memory/allocation.hpp"
   11.32 +#include "oops/oop.hpp"
   11.33 +#include "utilities/stack.hpp"
   11.34 +
   11.35 +class G1StringDedupUnlinkOrOopsDoClosure;
   11.36 +
   11.37 +//
   11.38 +// The deduplication queue acts as the communication channel between the stop-the-world
   11.39 +// mark/evacuation phase and the concurrent deduplication phase. Deduplication candidates
   11.40 +// found during mark/evacuation are placed on this queue for later processing in the
   11.41 +// deduplication thread. A queue entry is an oop pointing to a String object (as opposed
   11.42 +// to entries in the deduplication hashtable which points to character arrays).
   11.43 +//
   11.44 +// While users of the queue treat it as a single queue, it is implemented as a set of
   11.45 +// queues, one queue per GC worker thread, to allow lock-free and cache-friendly enqueue
   11.46 +// operations by the GC workers.
   11.47 +//
   11.48 +// The oops in the queue are treated as weak pointers, meaning the objects they point to
   11.49 +// can become unreachable and pruned (cleared) before being popped by the deduplication
   11.50 +// thread.
   11.51 +//
   11.52 +// Pushing to the queue is thread safe (this relies on each thread using a unique worker
   11.53 +// id), but only allowed during a safepoint. Popping from the queue is NOT thread safe
   11.54 +// and can only be done by the deduplication thread outside a safepoint.
   11.55 +//
   11.56 +// The StringDedupQueue_lock is only used for blocking and waking up the deduplication
   11.57 +// thread in case the queue is empty or becomes non-empty, respectively. This lock does
   11.58 +// not otherwise protect the queue content.
   11.59 +//
   11.60 +class G1StringDedupQueue : public CHeapObj<mtGC> {
   11.61 +private:
   11.62 +  typedef Stack<oop, mtGC> G1StringDedupWorkerQueue;
   11.63 +
   11.64 +  static G1StringDedupQueue* _queue;
   11.65 +  static const size_t        _max_size;
   11.66 +  static const size_t        _max_cache_size;
   11.67 +
   11.68 +  G1StringDedupWorkerQueue*  _queues;
   11.69 +  size_t                     _nqueues;
   11.70 +  size_t                     _cursor;
   11.71 +  volatile bool              _empty;
   11.72 +
   11.73 +  // Statistics counter, only used for logging.
   11.74 +  uintx                      _dropped;
   11.75 +
   11.76 +  G1StringDedupQueue();
   11.77 +  ~G1StringDedupQueue();
   11.78 +
   11.79 +  static void unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl, size_t queue);
   11.80 +
   11.81 +public:
   11.82 +  static void create();
   11.83 +
   11.84 +  // Blocks and waits for the queue to become non-empty.
   11.85 +  static void wait();
   11.86 +
   11.87 +  // Pushes a deduplication candidate onto a specific GC worker queue.
   11.88 +  static void push(uint worker_id, oop java_string);
   11.89 +
   11.90 +  // Pops a deduplication candidate from any queue, returns NULL if
   11.91 +  // all queues are empty.
   11.92 +  static oop pop();
   11.93 +
   11.94 +  static void unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl);
   11.95 +
   11.96 +  static void print_statistics(outputStream* st);
   11.97 +  static void verify();
   11.98 +};
   11.99 +
  11.100 +#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPQUEUE_HPP
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/src/share/vm/gc_implementation/g1/g1StringDedupStat.cpp	Tue Mar 18 19:07:22 2014 +0100
    12.3 @@ -0,0 +1,162 @@
    12.4 +/*
    12.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
    12.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    12.7 + *
    12.8 + * This code is free software; you can redistribute it and/or modify it
    12.9 + * under the terms of the GNU General Public License version 2 only, as
   12.10 + * published by the Free Software Foundation.
   12.11 + *
   12.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
   12.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   12.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   12.15 + * version 2 for more details (a copy is included in the LICENSE file that
   12.16 + * accompanied this code).
   12.17 + *
   12.18 + * You should have received a copy of the GNU General Public License version
   12.19 + * 2 along with this work; if not, write to the Free Software Foundation,
   12.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   12.21 + *
   12.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   12.23 + * or visit www.oracle.com if you need additional information or have any
   12.24 + * questions.
   12.25 + *
   12.26 + */
   12.27 +
   12.28 +#include "precompiled.hpp"
   12.29 +#include "gc_implementation/g1/g1StringDedupStat.hpp"
   12.30 +
   12.31 +G1StringDedupStat::G1StringDedupStat() :
   12.32 +  _inspected(0),
   12.33 +  _skipped(0),
   12.34 +  _hashed(0),
   12.35 +  _known(0),
   12.36 +  _new(0),
   12.37 +  _new_bytes(0),
   12.38 +  _deduped(0),
   12.39 +  _deduped_bytes(0),
   12.40 +  _deduped_young(0),
   12.41 +  _deduped_young_bytes(0),
   12.42 +  _deduped_old(0),
   12.43 +  _deduped_old_bytes(0),
   12.44 +  _idle(0),
   12.45 +  _exec(0),
   12.46 +  _block(0),
   12.47 +  _start(0.0),
   12.48 +  _idle_elapsed(0.0),
   12.49 +  _exec_elapsed(0.0),
   12.50 +  _block_elapsed(0.0) {
   12.51 +}
   12.52 +
   12.53 +void G1StringDedupStat::add(const G1StringDedupStat& stat) {
   12.54 +  _inspected           += stat._inspected;
   12.55 +  _skipped             += stat._skipped;
   12.56 +  _hashed              += stat._hashed;
   12.57 +  _known               += stat._known;
   12.58 +  _new                 += stat._new;
   12.59 +  _new_bytes           += stat._new_bytes;
   12.60 +  _deduped             += stat._deduped;
   12.61 +  _deduped_bytes       += stat._deduped_bytes;
   12.62 +  _deduped_young       += stat._deduped_young;
   12.63 +  _deduped_young_bytes += stat._deduped_young_bytes;
   12.64 +  _deduped_old         += stat._deduped_old;
   12.65 +  _deduped_old_bytes   += stat._deduped_old_bytes;
   12.66 +  _idle                += stat._idle;
   12.67 +  _exec                += stat._exec;
   12.68 +  _block               += stat._block;
   12.69 +  _idle_elapsed        += stat._idle_elapsed;
   12.70 +  _exec_elapsed        += stat._exec_elapsed;
   12.71 +  _block_elapsed       += stat._block_elapsed;
   12.72 +}
   12.73 +
   12.74 +void G1StringDedupStat::print_summary(outputStream* st, const G1StringDedupStat& last_stat, const G1StringDedupStat& total_stat) {
   12.75 +  double total_deduped_bytes_percent = 0.0;
   12.76 +
   12.77 +  if (total_stat._new_bytes > 0) {
   12.78 +    // Avoid division by zero
   12.79 +    total_deduped_bytes_percent = (double)total_stat._deduped_bytes / (double)total_stat._new_bytes * 100.0;
   12.80 +  }
   12.81 +
   12.82 +  st->date_stamp(PrintGCDateStamps);
   12.83 +  st->stamp(PrintGCTimeStamps);
   12.84 +  st->print_cr(
   12.85 +    "[GC concurrent-string-deduplication, "
   12.86 +    G1_STRDEDUP_BYTES_FORMAT_NS"->"G1_STRDEDUP_BYTES_FORMAT_NS"("G1_STRDEDUP_BYTES_FORMAT_NS"), avg "
   12.87 +    G1_STRDEDUP_PERCENT_FORMAT_NS", "G1_STRDEDUP_TIME_FORMAT"]",
   12.88 +    G1_STRDEDUP_BYTES_PARAM(last_stat._new_bytes),
   12.89 +    G1_STRDEDUP_BYTES_PARAM(last_stat._new_bytes - last_stat._deduped_bytes),
   12.90 +    G1_STRDEDUP_BYTES_PARAM(last_stat._deduped_bytes),
   12.91 +    total_deduped_bytes_percent,
   12.92 +    last_stat._exec_elapsed);
   12.93 +}
   12.94 +
   12.95 +void G1StringDedupStat::print_statistics(outputStream* st, const G1StringDedupStat& stat, bool total) {
   12.96 +  double young_percent               = 0.0;
   12.97 +  double old_percent                 = 0.0;
   12.98 +  double skipped_percent             = 0.0;
   12.99 +  double hashed_percent              = 0.0;
  12.100 +  double known_percent               = 0.0;
  12.101 +  double new_percent                 = 0.0;
  12.102 +  double deduped_percent             = 0.0;
  12.103 +  double deduped_bytes_percent       = 0.0;
  12.104 +  double deduped_young_percent       = 0.0;
  12.105 +  double deduped_young_bytes_percent = 0.0;
  12.106 +  double deduped_old_percent         = 0.0;
  12.107 +  double deduped_old_bytes_percent   = 0.0;
  12.108 +
  12.109 +  if (stat._inspected > 0) {
  12.110 +    // Avoid division by zero
  12.111 +    skipped_percent = (double)stat._skipped / (double)stat._inspected * 100.0;
  12.112 +    hashed_percent  = (double)stat._hashed / (double)stat._inspected * 100.0;
  12.113 +    known_percent   = (double)stat._known / (double)stat._inspected * 100.0;
  12.114 +    new_percent     = (double)stat._new / (double)stat._inspected * 100.0;
  12.115 +  }
  12.116 +
  12.117 +  if (stat._new > 0) {
  12.118 +    // Avoid division by zero
  12.119 +    deduped_percent = (double)stat._deduped / (double)stat._new * 100.0;
  12.120 +  }
  12.121 +
  12.122 +  if (stat._deduped > 0) {
  12.123 +    // Avoid division by zero
  12.124 +    deduped_young_percent = (double)stat._deduped_young / (double)stat._deduped * 100.0;
  12.125 +    deduped_old_percent   = (double)stat._deduped_old / (double)stat._deduped * 100.0;
  12.126 +  }
  12.127 +
  12.128 +  if (stat._new_bytes > 0) {
  12.129 +    // Avoid division by zero
  12.130 +    deduped_bytes_percent = (double)stat._deduped_bytes / (double)stat._new_bytes * 100.0;
  12.131 +  }
  12.132 +
  12.133 +  if (stat._deduped_bytes > 0) {
  12.134 +    // Avoid division by zero
  12.135 +    deduped_young_bytes_percent = (double)stat._deduped_young_bytes / (double)stat._deduped_bytes * 100.0;
  12.136 +    deduped_old_bytes_percent   = (double)stat._deduped_old_bytes / (double)stat._deduped_bytes * 100.0;
  12.137 +  }
  12.138 +
  12.139 +  if (total) {
  12.140 +    st->print_cr(
  12.141 +      "   [Total Exec: "UINTX_FORMAT"/"G1_STRDEDUP_TIME_FORMAT", Idle: "UINTX_FORMAT"/"G1_STRDEDUP_TIME_FORMAT", Blocked: "UINTX_FORMAT"/"G1_STRDEDUP_TIME_FORMAT"]",
  12.142 +      stat._exec, stat._exec_elapsed, stat._idle, stat._idle_elapsed, stat._block, stat._block_elapsed);
  12.143 +  } else {
  12.144 +    st->print_cr(
  12.145 +      "   [Last Exec: "G1_STRDEDUP_TIME_FORMAT", Idle: "G1_STRDEDUP_TIME_FORMAT", Blocked: "UINTX_FORMAT"/"G1_STRDEDUP_TIME_FORMAT"]",
  12.146 +      stat._exec_elapsed, stat._idle_elapsed, stat._block, stat._block_elapsed);
  12.147 +  }
  12.148 +  st->print_cr(
  12.149 +    "      [Inspected:    "G1_STRDEDUP_OBJECTS_FORMAT"]\n"
  12.150 +    "         [Skipped:   "G1_STRDEDUP_OBJECTS_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT")]\n"
  12.151 +    "         [Hashed:    "G1_STRDEDUP_OBJECTS_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT")]\n"
  12.152 +    "         [Known:     "G1_STRDEDUP_OBJECTS_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT")]\n"
  12.153 +    "         [New:       "G1_STRDEDUP_OBJECTS_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT") "G1_STRDEDUP_BYTES_FORMAT"]\n"
  12.154 +    "      [Deduplicated: "G1_STRDEDUP_OBJECTS_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT") "G1_STRDEDUP_BYTES_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT")]\n"
  12.155 +    "         [Young:     "G1_STRDEDUP_OBJECTS_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT") "G1_STRDEDUP_BYTES_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT")]\n"
  12.156 +    "         [Old:       "G1_STRDEDUP_OBJECTS_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT") "G1_STRDEDUP_BYTES_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT")]",
  12.157 +    stat._inspected,
  12.158 +    stat._skipped, skipped_percent,
  12.159 +    stat._hashed, hashed_percent,
  12.160 +    stat._known, known_percent,
  12.161 +    stat._new, new_percent, G1_STRDEDUP_BYTES_PARAM(stat._new_bytes),
  12.162 +    stat._deduped, deduped_percent, G1_STRDEDUP_BYTES_PARAM(stat._deduped_bytes), deduped_bytes_percent,
  12.163 +    stat._deduped_young, deduped_young_percent, G1_STRDEDUP_BYTES_PARAM(stat._deduped_young_bytes), deduped_young_bytes_percent,
  12.164 +    stat._deduped_old, deduped_old_percent, G1_STRDEDUP_BYTES_PARAM(stat._deduped_old_bytes), deduped_old_bytes_percent);
  12.165 +}
    13.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.2 +++ b/src/share/vm/gc_implementation/g1/g1StringDedupStat.hpp	Tue Mar 18 19:07:22 2014 +0100
    13.3 @@ -0,0 +1,142 @@
    13.4 +/*
    13.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
    13.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    13.7 + *
    13.8 + * This code is free software; you can redistribute it and/or modify it
    13.9 + * under the terms of the GNU General Public License version 2 only, as
   13.10 + * published by the Free Software Foundation.
   13.11 + *
   13.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
   13.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   13.15 + * version 2 for more details (a copy is included in the LICENSE file that
   13.16 + * accompanied this code).
   13.17 + *
   13.18 + * You should have received a copy of the GNU General Public License version
   13.19 + * 2 along with this work; if not, write to the Free Software Foundation,
   13.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   13.21 + *
   13.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   13.23 + * or visit www.oracle.com if you need additional information or have any
   13.24 + * questions.
   13.25 + *
   13.26 + */
   13.27 +
   13.28 +#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPSTAT_HPP
   13.29 +#define SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPSTAT_HPP
   13.30 +
   13.31 +#include "memory/allocation.hpp"
   13.32 +#include "runtime/os.hpp"
   13.33 +
   13.34 +// Macros for GC log output formating
   13.35 +#define G1_STRDEDUP_OBJECTS_FORMAT         UINTX_FORMAT_W(12)
   13.36 +#define G1_STRDEDUP_TIME_FORMAT            "%1.7lf secs"
   13.37 +#define G1_STRDEDUP_PERCENT_FORMAT         "%5.1lf%%"
   13.38 +#define G1_STRDEDUP_PERCENT_FORMAT_NS      "%.1lf%%"
   13.39 +#define G1_STRDEDUP_BYTES_FORMAT           "%8.1lf%s"
   13.40 +#define G1_STRDEDUP_BYTES_FORMAT_NS        "%.1lf%s"
   13.41 +#define G1_STRDEDUP_BYTES_PARAM(bytes)     byte_size_in_proper_unit((double)(bytes)), proper_unit_for_byte_size((bytes))
   13.42 +
   13.43 +//
   13.44 +// Statistics gathered by the deduplication thread.
   13.45 +//
   13.46 +class G1StringDedupStat : public StackObj {
   13.47 +private:
   13.48 +  // Counters
   13.49 +  uintx  _inspected;
   13.50 +  uintx  _skipped;
   13.51 +  uintx  _hashed;
   13.52 +  uintx  _known;
   13.53 +  uintx  _new;
   13.54 +  uintx  _new_bytes;
   13.55 +  uintx  _deduped;
   13.56 +  uintx  _deduped_bytes;
   13.57 +  uintx  _deduped_young;
   13.58 +  uintx  _deduped_young_bytes;
   13.59 +  uintx  _deduped_old;
   13.60 +  uintx  _deduped_old_bytes;
   13.61 +  uintx  _idle;
   13.62 +  uintx  _exec;
   13.63 +  uintx  _block;
   13.64 +
   13.65 +  // Time spent by the deduplication thread in different phases
   13.66 +  double _start;
   13.67 +  double _idle_elapsed;
   13.68 +  double _exec_elapsed;
   13.69 +  double _block_elapsed;
   13.70 +
   13.71 +public:
   13.72 +  G1StringDedupStat();
   13.73 +
   13.74 +  void inc_inspected() {
   13.75 +    _inspected++;
   13.76 +  }
   13.77 +
   13.78 +  void inc_skipped() {
   13.79 +    _skipped++;
   13.80 +  }
   13.81 +
   13.82 +  void inc_hashed() {
   13.83 +    _hashed++;
   13.84 +  }
   13.85 +
   13.86 +  void inc_known() {
   13.87 +    _known++;
   13.88 +  }
   13.89 +
   13.90 +  void inc_new(uintx bytes) {
   13.91 +    _new++;
   13.92 +    _new_bytes += bytes;
   13.93 +  }
   13.94 +
   13.95 +  void inc_deduped_young(uintx bytes) {
   13.96 +    _deduped++;
   13.97 +    _deduped_bytes += bytes;
   13.98 +    _deduped_young++;
   13.99 +    _deduped_young_bytes += bytes;
  13.100 +  }
  13.101 +
  13.102 +  void inc_deduped_old(uintx bytes) {
  13.103 +    _deduped++;
  13.104 +    _deduped_bytes += bytes;
  13.105 +    _deduped_old++;
  13.106 +    _deduped_old_bytes += bytes;
  13.107 +  }
  13.108 +
  13.109 +  void mark_idle() {
  13.110 +    _start = os::elapsedTime();
  13.111 +    _idle++;
  13.112 +  }
  13.113 +
  13.114 +  void mark_exec() {
  13.115 +    double now = os::elapsedTime();
  13.116 +    _idle_elapsed = now - _start;
  13.117 +    _start = now;
  13.118 +    _exec++;
  13.119 +  }
  13.120 +
  13.121 +  void mark_block() {
  13.122 +    double now = os::elapsedTime();
  13.123 +    _exec_elapsed += now - _start;
  13.124 +    _start = now;
  13.125 +    _block++;
  13.126 +  }
  13.127 +
  13.128 +  void mark_unblock() {
  13.129 +    double now = os::elapsedTime();
  13.130 +    _block_elapsed += now - _start;
  13.131 +    _start = now;
  13.132 +  }
  13.133 +
  13.134 +  void mark_done() {
  13.135 +    double now = os::elapsedTime();
  13.136 +    _exec_elapsed += now - _start;
  13.137 +  }
  13.138 +
  13.139 +  void add(const G1StringDedupStat& stat);
  13.140 +
  13.141 +  static void print_summary(outputStream* st, const G1StringDedupStat& last_stat, const G1StringDedupStat& total_stat);
  13.142 +  static void print_statistics(outputStream* st, const G1StringDedupStat& stat, bool total);
  13.143 +};
  13.144 +
  13.145 +#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPSTAT_HPP
    14.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.2 +++ b/src/share/vm/gc_implementation/g1/g1StringDedupTable.cpp	Tue Mar 18 19:07:22 2014 +0100
    14.3 @@ -0,0 +1,569 @@
    14.4 +/*
    14.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
    14.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    14.7 + *
    14.8 + * This code is free software; you can redistribute it and/or modify it
    14.9 + * under the terms of the GNU General Public License version 2 only, as
   14.10 + * published by the Free Software Foundation.
   14.11 + *
   14.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
   14.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   14.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14.15 + * version 2 for more details (a copy is included in the LICENSE file that
   14.16 + * accompanied this code).
   14.17 + *
   14.18 + * You should have received a copy of the GNU General Public License version
   14.19 + * 2 along with this work; if not, write to the Free Software Foundation,
   14.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   14.21 + *
   14.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   14.23 + * or visit www.oracle.com if you need additional information or have any
   14.24 + * questions.
   14.25 + *
   14.26 + */
   14.27 +
   14.28 +#include "precompiled.hpp"
   14.29 +#include "classfile/altHashing.hpp"
   14.30 +#include "classfile/javaClasses.hpp"
   14.31 +#include "gc_implementation/g1/g1CollectedHeap.inline.hpp"
   14.32 +#include "gc_implementation/g1/g1SATBCardTableModRefBS.hpp"
   14.33 +#include "gc_implementation/g1/g1StringDedupTable.hpp"
   14.34 +#include "memory/gcLocker.hpp"
   14.35 +#include "memory/padded.inline.hpp"
   14.36 +#include "oops/typeArrayOop.hpp"
   14.37 +#include "runtime/mutexLocker.hpp"
   14.38 +
   14.39 +//
   14.40 +// Freelist in the deduplication table entry cache. Links table
   14.41 +// entries together using their _next fields.
   14.42 +//
   14.43 +class G1StringDedupEntryFreeList : public CHeapObj<mtGC> {
   14.44 +private:
   14.45 +  G1StringDedupEntry* _list;
   14.46 +  size_t              _length;
   14.47 +
   14.48 +public:
   14.49 +  G1StringDedupEntryFreeList() :
   14.50 +    _list(NULL),
   14.51 +    _length(0) {
   14.52 +  }
   14.53 +
   14.54 +  void add(G1StringDedupEntry* entry) {
   14.55 +    entry->set_next(_list);
   14.56 +    _list = entry;
   14.57 +    _length++;
   14.58 +  }
   14.59 +
   14.60 +  G1StringDedupEntry* remove() {
   14.61 +    G1StringDedupEntry* entry = _list;
   14.62 +    if (entry != NULL) {
   14.63 +      _list = entry->next();
   14.64 +      _length--;
   14.65 +    }
   14.66 +    return entry;
   14.67 +  }
   14.68 +
   14.69 +  size_t length() {
   14.70 +    return _length;
   14.71 +  }
   14.72 +};
   14.73 +
   14.74 +//
   14.75 +// Cache of deduplication table entries. This cache provides fast allocation and
   14.76 +// reuse of table entries to lower the pressure on the underlying allocator.
   14.77 +// But more importantly, it provides fast/deferred freeing of table entries. This
   14.78 +// is important because freeing of table entries is done during stop-the-world
   14.79 +// phases and it is not uncommon for large number of entries to be freed at once.
   14.80 +// Tables entries that are freed during these phases are placed onto a freelist in
   14.81 +// the cache. The deduplication thread, which executes in a concurrent phase, will
   14.82 +// later reuse or free the underlying memory for these entries.
   14.83 +//
   14.84 +// The cache allows for single-threaded allocations and multi-threaded frees.
   14.85 +// Allocations are synchronized by StringDedupTable_lock as part of a table
   14.86 +// modification.
   14.87 +//
   14.88 +class G1StringDedupEntryCache : public CHeapObj<mtGC> {
   14.89 +private:
   14.90 +  // One freelist per GC worker to allow lock less freeing of
   14.91 +  // entries while doing a parallel scan of the table. Using
   14.92 +  // PaddedEnd to avoid false sharing.
   14.93 +  PaddedEnd<G1StringDedupEntryFreeList>* _lists;
   14.94 +  size_t                                 _nlists;
   14.95 +
   14.96 +public:
   14.97 +  G1StringDedupEntryCache();
   14.98 +  ~G1StringDedupEntryCache();
   14.99 +
  14.100 +  // Get a table entry from the cache freelist, or allocate a new
  14.101 +  // entry if the cache is empty.
  14.102 +  G1StringDedupEntry* alloc();
  14.103 +
  14.104 +  // Insert a table entry into the cache freelist.
  14.105 +  void free(G1StringDedupEntry* entry, uint worker_id);
  14.106 +
  14.107 +  // Returns current number of entries in the cache.
  14.108 +  size_t size();
  14.109 +
  14.110 +  // If the cache has grown above the given max size, trim it down
  14.111 +  // and deallocate the memory occupied by trimmed of entries.
  14.112 +  void trim(size_t max_size);
  14.113 +};
  14.114 +
  14.115 +G1StringDedupEntryCache::G1StringDedupEntryCache() {
  14.116 +  _nlists = MAX2(ParallelGCThreads, (size_t)1);
  14.117 +  _lists = PaddedArray<G1StringDedupEntryFreeList, mtGC>::create_unfreeable((uint)_nlists);
  14.118 +}
  14.119 +
  14.120 +G1StringDedupEntryCache::~G1StringDedupEntryCache() {
  14.121 +  ShouldNotReachHere();
  14.122 +}
  14.123 +
  14.124 +G1StringDedupEntry* G1StringDedupEntryCache::alloc() {
  14.125 +  for (size_t i = 0; i < _nlists; i++) {
  14.126 +    G1StringDedupEntry* entry = _lists[i].remove();
  14.127 +    if (entry != NULL) {
  14.128 +      return entry;
  14.129 +    }
  14.130 +  }
  14.131 +  return new G1StringDedupEntry();
  14.132 +}
  14.133 +
  14.134 +void G1StringDedupEntryCache::free(G1StringDedupEntry* entry, uint worker_id) {
  14.135 +  assert(entry->obj() != NULL, "Double free");
  14.136 +  assert(worker_id < _nlists, "Invalid worker id");
  14.137 +  entry->set_obj(NULL);
  14.138 +  entry->set_hash(0);
  14.139 +  _lists[worker_id].add(entry);
  14.140 +}
  14.141 +
  14.142 +size_t G1StringDedupEntryCache::size() {
  14.143 +  size_t size = 0;
  14.144 +  for (size_t i = 0; i < _nlists; i++) {
  14.145 +    size += _lists[i].length();
  14.146 +  }
  14.147 +  return size;
  14.148 +}
  14.149 +
  14.150 +void G1StringDedupEntryCache::trim(size_t max_size) {
  14.151 +  size_t cache_size = 0;
  14.152 +  for (size_t i = 0; i < _nlists; i++) {
  14.153 +    G1StringDedupEntryFreeList* list = &_lists[i];
  14.154 +    cache_size += list->length();
  14.155 +    while (cache_size > max_size) {
  14.156 +      G1StringDedupEntry* entry = list->remove();
  14.157 +      assert(entry != NULL, "Should not be null");
  14.158 +      cache_size--;
  14.159 +      delete entry;
  14.160 +    }
  14.161 +  }
  14.162 +}
  14.163 +
  14.164 +G1StringDedupTable*      G1StringDedupTable::_table = NULL;
  14.165 +G1StringDedupEntryCache* G1StringDedupTable::_entry_cache = NULL;
  14.166 +
  14.167 +const size_t             G1StringDedupTable::_min_size = (1 << 10);   // 1024
  14.168 +const size_t             G1StringDedupTable::_max_size = (1 << 24);   // 16777216
  14.169 +const double             G1StringDedupTable::_grow_load_factor = 2.0; // Grow table at 200% load
  14.170 +const double             G1StringDedupTable::_shrink_load_factor = _grow_load_factor / 3.0; // Shrink table at 67% load
  14.171 +const double             G1StringDedupTable::_max_cache_factor = 0.1; // Cache a maximum of 10% of the table size
  14.172 +const uintx              G1StringDedupTable::_rehash_multiple = 60;   // Hash bucket has 60 times more collisions than expected
  14.173 +const uintx              G1StringDedupTable::_rehash_threshold = (uintx)(_rehash_multiple * _grow_load_factor);
  14.174 +
  14.175 +uintx                    G1StringDedupTable::_entries_added = 0;
  14.176 +uintx                    G1StringDedupTable::_entries_removed = 0;
  14.177 +uintx                    G1StringDedupTable::_resize_count = 0;
  14.178 +uintx                    G1StringDedupTable::_rehash_count = 0;
  14.179 +
  14.180 +G1StringDedupTable::G1StringDedupTable(size_t size, jint hash_seed) :
  14.181 +  _size(size),
  14.182 +  _entries(0),
  14.183 +  _grow_threshold((uintx)(size * _grow_load_factor)),
  14.184 +  _shrink_threshold((uintx)(size * _shrink_load_factor)),
  14.185 +  _rehash_needed(false),
  14.186 +  _hash_seed(hash_seed) {
  14.187 +  assert(is_power_of_2(size), "Table size must be a power of 2");
  14.188 +  _buckets = NEW_C_HEAP_ARRAY(G1StringDedupEntry*, _size, mtGC);
  14.189 +  memset(_buckets, 0, _size * sizeof(G1StringDedupEntry*));
  14.190 +}
  14.191 +
  14.192 +G1StringDedupTable::~G1StringDedupTable() {
  14.193 +  FREE_C_HEAP_ARRAY(G1StringDedupEntry*, _buckets, mtGC);
  14.194 +}
  14.195 +
  14.196 +void G1StringDedupTable::create() {
  14.197 +  assert(_table == NULL, "One string deduplication table allowed");
  14.198 +  _entry_cache = new G1StringDedupEntryCache();
  14.199 +  _table = new G1StringDedupTable(_min_size);
  14.200 +}
  14.201 +
  14.202 +void G1StringDedupTable::add(typeArrayOop value, unsigned int hash, G1StringDedupEntry** list) {
  14.203 +  G1StringDedupEntry* entry = _entry_cache->alloc();
  14.204 +  entry->set_obj(value);
  14.205 +  entry->set_hash(hash);
  14.206 +  entry->set_next(*list);
  14.207 +  *list = entry;
  14.208 +  _entries++;
  14.209 +}
  14.210 +
  14.211 +void G1StringDedupTable::remove(G1StringDedupEntry** pentry, uint worker_id) {
  14.212 +  G1StringDedupEntry* entry = *pentry;
  14.213 +  *pentry = entry->next();
  14.214 +  _entry_cache->free(entry, worker_id);
  14.215 +}
  14.216 +
  14.217 +void G1StringDedupTable::transfer(G1StringDedupEntry** pentry, G1StringDedupTable* dest) {
  14.218 +  G1StringDedupEntry* entry = *pentry;
  14.219 +  *pentry = entry->next();
  14.220 +  unsigned int hash = entry->hash();
  14.221 +  size_t index = dest->hash_to_index(hash);
  14.222 +  G1StringDedupEntry** list = dest->bucket(index);
  14.223 +  entry->set_next(*list);
  14.224 +  *list = entry;
  14.225 +}
  14.226 +
  14.227 +bool G1StringDedupTable::equals(typeArrayOop value1, typeArrayOop value2) {
  14.228 +  return (value1 == value2 ||
  14.229 +          (value1->length() == value2->length() &&
  14.230 +           (!memcmp(value1->base(T_CHAR),
  14.231 +                    value2->base(T_CHAR),
  14.232 +                    value1->length() * sizeof(jchar)))));
  14.233 +}
  14.234 +
  14.235 +typeArrayOop G1StringDedupTable::lookup(typeArrayOop value, unsigned int hash,
  14.236 +                                        G1StringDedupEntry** list, uintx &count) {
  14.237 +  for (G1StringDedupEntry* entry = *list; entry != NULL; entry = entry->next()) {
  14.238 +    if (entry->hash() == hash) {
  14.239 +      typeArrayOop existing_value = entry->obj();
  14.240 +      if (equals(value, existing_value)) {
  14.241 +        // Match found
  14.242 +        return existing_value;
  14.243 +      }
  14.244 +    }
  14.245 +    count++;
  14.246 +  }
  14.247 +
  14.248 +  // Not found
  14.249 +  return NULL;
  14.250 +}
  14.251 +
  14.252 +typeArrayOop G1StringDedupTable::lookup_or_add_inner(typeArrayOop value, unsigned int hash) {
  14.253 +  size_t index = hash_to_index(hash);
  14.254 +  G1StringDedupEntry** list = bucket(index);
  14.255 +  uintx count = 0;
  14.256 +
  14.257 +  // Lookup in list
  14.258 +  typeArrayOop existing_value = lookup(value, hash, list, count);
  14.259 +
  14.260 +  // Check if rehash is needed
  14.261 +  if (count > _rehash_threshold) {
  14.262 +    _rehash_needed = true;
  14.263 +  }
  14.264 +
  14.265 +  if (existing_value == NULL) {
  14.266 +    // Not found, add new entry
  14.267 +    add(value, hash, list);
  14.268 +
  14.269 +    // Update statistics
  14.270 +    _entries_added++;
  14.271 +  }
  14.272 +
  14.273 +  return existing_value;
  14.274 +}
  14.275 +
  14.276 +unsigned int G1StringDedupTable::hash_code(typeArrayOop value) {
  14.277 +  unsigned int hash;
  14.278 +  int length = value->length();
  14.279 +  const jchar* data = (jchar*)value->base(T_CHAR);
  14.280 +
  14.281 +  if (use_java_hash()) {
  14.282 +    hash = java_lang_String::hash_code(data, length);
  14.283 +  } else {
  14.284 +    hash = AltHashing::murmur3_32(_table->_hash_seed, data, length);
  14.285 +  }
  14.286 +
  14.287 +  return hash;
  14.288 +}
  14.289 +
  14.290 +void G1StringDedupTable::deduplicate(oop java_string, G1StringDedupStat& stat) {
  14.291 +  assert(java_lang_String::is_instance(java_string), "Must be a string");
  14.292 +  No_Safepoint_Verifier nsv;
  14.293 +
  14.294 +  stat.inc_inspected();
  14.295 +
  14.296 +  typeArrayOop value = java_lang_String::value(java_string);
  14.297 +  if (value == NULL) {
  14.298 +    // String has no value
  14.299 +    stat.inc_skipped();
  14.300 +    return;
  14.301 +  }
  14.302 +
  14.303 +  unsigned int hash = 0;
  14.304 +
  14.305 +  if (use_java_hash()) {
  14.306 +    // Get hash code from cache
  14.307 +    hash = java_lang_String::hash(java_string);
  14.308 +  }
  14.309 +
  14.310 +  if (hash == 0) {
  14.311 +    // Compute hash
  14.312 +    hash = hash_code(value);
  14.313 +    stat.inc_hashed();
  14.314 +  }
  14.315 +
  14.316 +  if (use_java_hash() && hash != 0) {
  14.317 +    // Store hash code in cache
  14.318 +    java_lang_String::set_hash(java_string, hash);
  14.319 +  }
  14.320 +
  14.321 +  typeArrayOop existing_value = lookup_or_add(value, hash);
  14.322 +  if (existing_value == value) {
  14.323 +    // Same value, already known
  14.324 +    stat.inc_known();
  14.325 +    return;
  14.326 +  }
  14.327 +
  14.328 +  // Get size of value array
  14.329 +  uintx size_in_bytes = value->size() * HeapWordSize;
  14.330 +  stat.inc_new(size_in_bytes);
  14.331 +
  14.332 +  if (existing_value != NULL) {
  14.333 +    // Enqueue the reference to make sure it is kept alive. Concurrent mark might
  14.334 +    // otherwise declare it dead if there are no other strong references to this object.
  14.335 +    G1SATBCardTableModRefBS::enqueue(existing_value);
  14.336 +
  14.337 +    // Existing value found, deduplicate string
  14.338 +    java_lang_String::set_value(java_string, existing_value);
  14.339 +
  14.340 +    if (G1CollectedHeap::heap()->is_in_young(value)) {
  14.341 +      stat.inc_deduped_young(size_in_bytes);
  14.342 +    } else {
  14.343 +      stat.inc_deduped_old(size_in_bytes);
  14.344 +    }
  14.345 +  }
  14.346 +}
  14.347 +
  14.348 +G1StringDedupTable* G1StringDedupTable::prepare_resize() {
  14.349 +  size_t size = _table->_size;
  14.350 +
  14.351 +  // Check if the hashtable needs to be resized
  14.352 +  if (_table->_entries > _table->_grow_threshold) {
  14.353 +    // Grow table, double the size
  14.354 +    size *= 2;
  14.355 +    if (size > _max_size) {
  14.356 +      // Too big, don't resize
  14.357 +      return NULL;
  14.358 +    }
  14.359 +  } else if (_table->_entries < _table->_shrink_threshold) {
  14.360 +    // Shrink table, half the size
  14.361 +    size /= 2;
  14.362 +    if (size < _min_size) {
  14.363 +      // Too small, don't resize
  14.364 +      return NULL;
  14.365 +    }
  14.366 +  } else if (StringDeduplicationResizeALot) {
  14.367 +    // Force grow
  14.368 +    size *= 2;
  14.369 +    if (size > _max_size) {
  14.370 +      // Too big, force shrink instead
  14.371 +      size /= 4;
  14.372 +    }
  14.373 +  } else {
  14.374 +    // Resize not needed
  14.375 +    return NULL;
  14.376 +  }
  14.377 +
  14.378 +  // Update statistics
  14.379 +  _resize_count++;
  14.380 +
  14.381 +  // Allocate the new table. The new table will be populated by workers
  14.382 +  // calling unlink_or_oops_do() and finally installed by finish_resize().
  14.383 +  return new G1StringDedupTable(size, _table->_hash_seed);
  14.384 +}
  14.385 +
  14.386 +void G1StringDedupTable::finish_resize(G1StringDedupTable* resized_table) {
  14.387 +  assert(resized_table != NULL, "Invalid table");
  14.388 +
  14.389 +  resized_table->_entries = _table->_entries;
  14.390 +
  14.391 +  // Free old table
  14.392 +  delete _table;
  14.393 +
  14.394 +  // Install new table
  14.395 +  _table = resized_table;
  14.396 +}
  14.397 +
  14.398 +void G1StringDedupTable::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl, uint worker_id) {
  14.399 +  // The table is divided into partitions to allow lock-less parallel processing by
  14.400 +  // multiple worker threads. A worker thread first claims a partition, which ensures
  14.401 +  // exclusive access to that part of the table, then continues to process it. To allow
  14.402 +  // shrinking of the table in parallel we also need to make sure that the same worker
  14.403 +  // thread processes all partitions where entries will hash to the same destination
  14.404 +  // partition. Since the table size is always a power of two and we always shrink by
  14.405 +  // dividing the table in half, we know that for a given partition there is only one
  14.406 +  // other partition whoes entries will hash to the same destination partition. That
  14.407 +  // other partition is always the sibling partition in the second half of the table.
  14.408 +  // For example, if the table is divided into 8 partitions, the sibling of partition 0
  14.409 +  // is partition 4, the sibling of partition 1 is partition 5, etc.
  14.410 +  size_t table_half = _table->_size / 2;
  14.411 +
  14.412 +  // Let each partition be one page worth of buckets
  14.413 +  size_t partition_size = MIN2(table_half, os::vm_page_size() / sizeof(G1StringDedupEntry*));
  14.414 +  assert(table_half % partition_size == 0, "Invalid partition size");
  14.415 +
  14.416 +  // Number of entries removed during the scan
  14.417 +  uintx removed = 0;
  14.418 +
  14.419 +  for (;;) {
  14.420 +    // Grab next partition to scan
  14.421 +    size_t partition_begin = cl->claim_table_partition(partition_size);
  14.422 +    size_t partition_end = partition_begin + partition_size;
  14.423 +    if (partition_begin >= table_half) {
  14.424 +      // End of table
  14.425 +      break;
  14.426 +    }
  14.427 +
  14.428 +    // Scan the partition followed by the sibling partition in the second half of the table
  14.429 +    removed += unlink_or_oops_do(cl, partition_begin, partition_end, worker_id);
  14.430 +    removed += unlink_or_oops_do(cl, table_half + partition_begin, table_half + partition_end, worker_id);
  14.431 +  }
  14.432 +
  14.433 +  // Delayed update avoid contention on the table lock
  14.434 +  if (removed > 0) {
  14.435 +    MutexLockerEx ml(StringDedupTable_lock, Mutex::_no_safepoint_check_flag);
  14.436 +    _table->_entries -= removed;
  14.437 +    _entries_removed += removed;
  14.438 +  }
  14.439 +}
  14.440 +
  14.441 +uintx G1StringDedupTable::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl,
  14.442 +                                            size_t partition_begin,
  14.443 +                                            size_t partition_end,
  14.444 +                                            uint worker_id) {
  14.445 +  uintx removed = 0;
  14.446 +  for (size_t bucket = partition_begin; bucket < partition_end; bucket++) {
  14.447 +    G1StringDedupEntry** entry = _table->bucket(bucket);
  14.448 +    while (*entry != NULL) {
  14.449 +      oop* p = (oop*)(*entry)->obj_addr();
  14.450 +      if (cl->is_alive(*p)) {
  14.451 +        cl->keep_alive(p);
  14.452 +        if (cl->is_resizing()) {
  14.453 +          // We are resizing the table, transfer entry to the new table
  14.454 +          _table->transfer(entry, cl->resized_table());
  14.455 +        } else {
  14.456 +          if (cl->is_rehashing()) {
  14.457 +            // We are rehashing the table, rehash the entry but keep it
  14.458 +            // in the table. We can't transfer entries into the new table
  14.459 +            // at this point since we don't have exclusive access to all
  14.460 +            // destination partitions. finish_rehash() will do a single
  14.461 +            // threaded transfer of all entries.
  14.462 +            typeArrayOop value = (typeArrayOop)*p;
  14.463 +            unsigned int hash = hash_code(value);
  14.464 +            (*entry)->set_hash(hash);
  14.465 +          }
  14.466 +
  14.467 +          // Move to next entry
  14.468 +          entry = (*entry)->next_addr();
  14.469 +        }
  14.470 +      } else {
  14.471 +        // Not alive, remove entry from table
  14.472 +        _table->remove(entry, worker_id);
  14.473 +        removed++;
  14.474 +      }
  14.475 +    }
  14.476 +  }
  14.477 +
  14.478 +  return removed;
  14.479 +}
  14.480 +
  14.481 +G1StringDedupTable* G1StringDedupTable::prepare_rehash() {
  14.482 +  if (!_table->_rehash_needed && !StringDeduplicationRehashALot) {
  14.483 +    // Rehash not needed
  14.484 +    return NULL;
  14.485 +  }
  14.486 +
  14.487 +  // Update statistics
  14.488 +  _rehash_count++;
  14.489 +
  14.490 +  // Compute new hash seed
  14.491 +  _table->_hash_seed = AltHashing::compute_seed();
  14.492 +
  14.493 +  // Allocate the new table, same size and hash seed
  14.494 +  return new G1StringDedupTable(_table->_size, _table->_hash_seed);
  14.495 +}
  14.496 +
  14.497 +void G1StringDedupTable::finish_rehash(G1StringDedupTable* rehashed_table) {
  14.498 +  assert(rehashed_table != NULL, "Invalid table");
  14.499 +
  14.500 +  // Move all newly rehashed entries into the correct buckets in the new table
  14.501 +  for (size_t bucket = 0; bucket < _table->_size; bucket++) {
  14.502 +    G1StringDedupEntry** entry = _table->bucket(bucket);
  14.503 +    while (*entry != NULL) {
  14.504 +      _table->transfer(entry, rehashed_table);
  14.505 +    }
  14.506 +  }
  14.507 +
  14.508 +  rehashed_table->_entries = _table->_entries;
  14.509 +
  14.510 +  // Free old table
  14.511 +  delete _table;
  14.512 +
  14.513 +  // Install new table
  14.514 +  _table = rehashed_table;
  14.515 +}
  14.516 +
  14.517 +void G1StringDedupTable::verify() {
  14.518 +  for (size_t bucket = 0; bucket < _table->_size; bucket++) {
  14.519 +    // Verify entries
  14.520 +    G1StringDedupEntry** entry = _table->bucket(bucket);
  14.521 +    while (*entry != NULL) {
  14.522 +      typeArrayOop value = (*entry)->obj();
  14.523 +      guarantee(value != NULL, "Object must not be NULL");
  14.524 +      guarantee(Universe::heap()->is_in_reserved(value), "Object must be on the heap");
  14.525 +      guarantee(!value->is_forwarded(), "Object must not be forwarded");
  14.526 +      guarantee(value->is_typeArray(), "Object must be a typeArrayOop");
  14.527 +      unsigned int hash = hash_code(value);
  14.528 +      guarantee((*entry)->hash() == hash, "Table entry has inorrect hash");
  14.529 +      guarantee(_table->hash_to_index(hash) == bucket, "Table entry has incorrect index");
  14.530 +      entry = (*entry)->next_addr();
  14.531 +    }
  14.532 +
  14.533 +    // Verify that we do not have entries with identical oops or identical arrays.
  14.534 +    // We only need to compare entries in the same bucket. If the same oop or an
  14.535 +    // identical array has been inserted more than once into different/incorrect
  14.536 +    // buckets the verification step above will catch that.
  14.537 +    G1StringDedupEntry** entry1 = _table->bucket(bucket);
  14.538 +    while (*entry1 != NULL) {
  14.539 +      typeArrayOop value1 = (*entry1)->obj();
  14.540 +      G1StringDedupEntry** entry2 = (*entry1)->next_addr();
  14.541 +      while (*entry2 != NULL) {
  14.542 +        typeArrayOop value2 = (*entry2)->obj();
  14.543 +        guarantee(!equals(value1, value2), "Table entries must not have identical arrays");
  14.544 +        entry2 = (*entry2)->next_addr();
  14.545 +      }
  14.546 +      entry1 = (*entry1)->next_addr();
  14.547 +    }
  14.548 +  }
  14.549 +}
  14.550 +
  14.551 +void G1StringDedupTable::trim_entry_cache() {
  14.552 +  MutexLockerEx ml(StringDedupTable_lock, Mutex::_no_safepoint_check_flag);
  14.553 +  size_t max_cache_size = (size_t)(_table->_size * _max_cache_factor);
  14.554 +  _entry_cache->trim(max_cache_size);
  14.555 +}
  14.556 +
  14.557 +void G1StringDedupTable::print_statistics(outputStream* st) {
  14.558 +  st->print_cr(
  14.559 +    "   [Table]\n"
  14.560 +    "      [Memory Usage: "G1_STRDEDUP_BYTES_FORMAT_NS"]\n"
  14.561 +    "      [Size: "SIZE_FORMAT", Min: "SIZE_FORMAT", Max: "SIZE_FORMAT"]\n"
  14.562 +    "      [Entries: "UINTX_FORMAT", Load: "G1_STRDEDUP_PERCENT_FORMAT_NS", Cached: " UINTX_FORMAT ", Added: "UINTX_FORMAT", Removed: "UINTX_FORMAT"]\n"
  14.563 +    "      [Resize Count: "UINTX_FORMAT", Shrink Threshold: "UINTX_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT_NS"), Grow Threshold: "UINTX_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT_NS")]\n"
  14.564 +    "      [Rehash Count: "UINTX_FORMAT", Rehash Threshold: "UINTX_FORMAT", Hash Seed: 0x%x]\n"
  14.565 +    "      [Age Threshold: "UINTX_FORMAT"]",
  14.566 +    G1_STRDEDUP_BYTES_PARAM(_table->_size * sizeof(G1StringDedupEntry*) + (_table->_entries + _entry_cache->size()) * sizeof(G1StringDedupEntry)),
  14.567 +    _table->_size, _min_size, _max_size,
  14.568 +    _table->_entries, (double)_table->_entries / (double)_table->_size * 100.0, _entry_cache->size(), _entries_added, _entries_removed,
  14.569 +    _resize_count, _table->_shrink_threshold, _shrink_load_factor * 100.0, _table->_grow_threshold, _grow_load_factor * 100.0,
  14.570 +    _rehash_count, _rehash_threshold, _table->_hash_seed,
  14.571 +    StringDeduplicationAgeThreshold);
  14.572 +}
    15.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.2 +++ b/src/share/vm/gc_implementation/g1/g1StringDedupTable.hpp	Tue Mar 18 19:07:22 2014 +0100
    15.3 @@ -0,0 +1,230 @@
    15.4 +/*
    15.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
    15.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    15.7 + *
    15.8 + * This code is free software; you can redistribute it and/or modify it
    15.9 + * under the terms of the GNU General Public License version 2 only, as
   15.10 + * published by the Free Software Foundation.
   15.11 + *
   15.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
   15.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   15.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   15.15 + * version 2 for more details (a copy is included in the LICENSE file that
   15.16 + * accompanied this code).
   15.17 + *
   15.18 + * You should have received a copy of the GNU General Public License version
   15.19 + * 2 along with this work; if not, write to the Free Software Foundation,
   15.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   15.21 + *
   15.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   15.23 + * or visit www.oracle.com if you need additional information or have any
   15.24 + * questions.
   15.25 + *
   15.26 + */
   15.27 +
   15.28 +#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPTABLE_HPP
   15.29 +#define SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPTABLE_HPP
   15.30 +
   15.31 +#include "gc_implementation/g1/g1StringDedupStat.hpp"
   15.32 +#include "runtime/mutexLocker.hpp"
   15.33 +
   15.34 +class G1StringDedupEntryCache;
   15.35 +
   15.36 +//
   15.37 +// Table entry in the deduplication hashtable. Points weakly to the
   15.38 +// character array. Can be chained in a linked list in case of hash
   15.39 +// collisions or when placed in a freelist in the entry cache.
   15.40 +//
   15.41 +class G1StringDedupEntry : public CHeapObj<mtGC> {
   15.42 +private:
   15.43 +  G1StringDedupEntry* _next;
   15.44 +  unsigned int      _hash;
   15.45 +  typeArrayOop      _obj;
   15.46 +
   15.47 +public:
   15.48 +  G1StringDedupEntry() :
   15.49 +    _next(NULL),
   15.50 +    _hash(0),
   15.51 +    _obj(NULL) {
   15.52 +  }
   15.53 +
   15.54 +  G1StringDedupEntry* next() {
   15.55 +    return _next;
   15.56 +  }
   15.57 +
   15.58 +  G1StringDedupEntry** next_addr() {
   15.59 +    return &_next;
   15.60 +  }
   15.61 +
   15.62 +  void set_next(G1StringDedupEntry* next) {
   15.63 +    _next = next;
   15.64 +  }
   15.65 +
   15.66 +  unsigned int hash() {
   15.67 +    return _hash;
   15.68 +  }
   15.69 +
   15.70 +  void set_hash(unsigned int hash) {
   15.71 +    _hash = hash;
   15.72 +  }
   15.73 +
   15.74 +  typeArrayOop obj() {
   15.75 +    return _obj;
   15.76 +  }
   15.77 +
   15.78 +  typeArrayOop* obj_addr() {
   15.79 +    return &_obj;
   15.80 +  }
   15.81 +
   15.82 +  void set_obj(typeArrayOop obj) {
   15.83 +    _obj = obj;
   15.84 +  }
   15.85 +};
   15.86 +
   15.87 +//
   15.88 +// The deduplication hashtable keeps track of all unique character arrays used
   15.89 +// by String objects. Each table entry weakly points to an character array, allowing
   15.90 +// otherwise unreachable character arrays to be declared dead and pruned from the
   15.91 +// table.
   15.92 +//
   15.93 +// The table is dynamically resized to accommodate the current number of table entries.
   15.94 +// The table has hash buckets with chains for hash collision. If the average chain
   15.95 +// length goes above or below given thresholds the table grows or shrinks accordingly.
   15.96 +//
   15.97 +// The table is also dynamically rehashed (using a new hash seed) if it becomes severely
   15.98 +// unbalanced, i.e., a hash chain is significantly longer than average.
   15.99 +//
  15.100 +// All access to the table is protected by the StringDedupTable_lock, except under
  15.101 +// safepoints in which case GC workers are allowed to access a table partitions they
  15.102 +// have claimed without first acquiring the lock. Note however, that this applies only
  15.103 +// the table partition (i.e. a range of elements in _buckets), not other parts of the
  15.104 +// table such as the _entries field, statistics counters, etc.
  15.105 +//
  15.106 +class G1StringDedupTable : public CHeapObj<mtGC> {
  15.107 +private:
  15.108 +  // The currently active hashtable instance. Only modified when
  15.109 +  // the table is resizes or rehashed.
  15.110 +  static G1StringDedupTable*      _table;
  15.111 +
  15.112 +  // Cache for reuse and fast alloc/free of table entries.
  15.113 +  static G1StringDedupEntryCache* _entry_cache;
  15.114 +
  15.115 +  G1StringDedupEntry**            _buckets;
  15.116 +  size_t                          _size;
  15.117 +  uintx                           _entries;
  15.118 +  uintx                           _shrink_threshold;
  15.119 +  uintx                           _grow_threshold;
  15.120 +  bool                            _rehash_needed;
  15.121 +
  15.122 +  // The hash seed also dictates which hash function to use. A
  15.123 +  // zero hash seed means we will use the Java compatible hash
  15.124 +  // function (which doesn't use a seed), and a non-zero hash
  15.125 +  // seed means we use the murmur3 hash function.
  15.126 +  jint                            _hash_seed;
  15.127 +
  15.128 +  // Constants governing table resize/rehash/cache.
  15.129 +  static const size_t             _min_size;
  15.130 +  static const size_t             _max_size;
  15.131 +  static const double             _grow_load_factor;
  15.132 +  static const double             _shrink_load_factor;
  15.133 +  static const uintx              _rehash_multiple;
  15.134 +  static const uintx              _rehash_threshold;
  15.135 +  static const double             _max_cache_factor;
  15.136 +
  15.137 +  // Table statistics, only used for logging.
  15.138 +  static uintx                    _entries_added;
  15.139 +  static uintx                    _entries_removed;
  15.140 +  static uintx                    _resize_count;
  15.141 +  static uintx                    _rehash_count;
  15.142 +
  15.143 +  G1StringDedupTable(size_t size, jint hash_seed = 0);
  15.144 +  ~G1StringDedupTable();
  15.145 +
  15.146 +  // Returns the hash bucket at the given index.
  15.147 +  G1StringDedupEntry** bucket(size_t index) {
  15.148 +    return _buckets + index;
  15.149 +  }
  15.150 +
  15.151 +  // Returns the hash bucket index for the given hash code.
  15.152 +  size_t hash_to_index(unsigned int hash) {
  15.153 +    return (size_t)hash & (_size - 1);
  15.154 +  }
  15.155 +
  15.156 +  // Adds a new table entry to the given hash bucket.
  15.157 +  void add(typeArrayOop value, unsigned int hash, G1StringDedupEntry** list);
  15.158 +
  15.159 +  // Removes the given table entry from the table.
  15.160 +  void remove(G1StringDedupEntry** pentry, uint worker_id);
  15.161 +
  15.162 +  // Transfers a table entry from the current table to the destination table.
  15.163 +  void transfer(G1StringDedupEntry** pentry, G1StringDedupTable* dest);
  15.164 +
  15.165 +  // Returns an existing character array in the given hash bucket, or NULL
  15.166 +  // if no matching character array exists.
  15.167 +  typeArrayOop lookup(typeArrayOop value, unsigned int hash,
  15.168 +                      G1StringDedupEntry** list, uintx &count);
  15.169 +
  15.170 +  // Returns an existing character array in the table, or inserts a new
  15.171 +  // table entry if no matching character array exists.
  15.172 +  typeArrayOop lookup_or_add_inner(typeArrayOop value, unsigned int hash);
  15.173 +
  15.174 +  // Thread safe lookup or add of table entry
  15.175 +  static typeArrayOop lookup_or_add(typeArrayOop value, unsigned int hash) {
  15.176 +    // Protect the table from concurrent access. Also note that this lock
  15.177 +    // acts as a fence for _table, which could have been replaced by a new
  15.178 +    // instance if the table was resized or rehashed.
  15.179 +    MutexLockerEx ml(StringDedupTable_lock, Mutex::_no_safepoint_check_flag);
  15.180 +    return _table->lookup_or_add_inner(value, hash);
  15.181 +  }
  15.182 +
  15.183 +  // Returns true if the hashtable is currently using a Java compatible
  15.184 +  // hash function.
  15.185 +  static bool use_java_hash() {
  15.186 +    return _table->_hash_seed == 0;
  15.187 +  }
  15.188 +
  15.189 +  static bool equals(typeArrayOop value1, typeArrayOop value2);
  15.190 +
  15.191 +  // Computes the hash code for the given character array, using the
  15.192 +  // currently active hash function and hash seed.
  15.193 +  static unsigned int hash_code(typeArrayOop value);
  15.194 +
  15.195 +  static uintx unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl,
  15.196 +                                 size_t partition_begin,
  15.197 +                                 size_t partition_end,
  15.198 +                                 uint worker_id);
  15.199 +
  15.200 +public:
  15.201 +  static void create();
  15.202 +
  15.203 +  // Deduplicates the given String object, or adds its backing
  15.204 +  // character array to the deduplication hashtable.
  15.205 +  static void deduplicate(oop java_string, G1StringDedupStat& stat);
  15.206 +
  15.207 +  // If a table resize is needed, returns a newly allocated empty
  15.208 +  // hashtable of the proper size.
  15.209 +  static G1StringDedupTable* prepare_resize();
  15.210 +
  15.211 +  // Installs a newly resized table as the currently active table
  15.212 +  // and deletes the previously active table.
  15.213 +  static void finish_resize(G1StringDedupTable* resized_table);
  15.214 +
  15.215 +  // If a table rehash is needed, returns a newly allocated empty
  15.216 +  // hashtable and updates the hash seed.
  15.217 +  static G1StringDedupTable* prepare_rehash();
  15.218 +
  15.219 +  // Transfers rehashed entries from the currently active table into
  15.220 +  // the new table. Installs the new table as the currently active table
  15.221 +  // and deletes the previously active table.
  15.222 +  static void finish_rehash(G1StringDedupTable* rehashed_table);
  15.223 +
  15.224 +  // If the table entry cache has grown too large, trim it down according to policy
  15.225 +  static void trim_entry_cache();
  15.226 +
  15.227 +  static void unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl, uint worker_id);
  15.228 +
  15.229 +  static void print_statistics(outputStream* st);
  15.230 +  static void verify();
  15.231 +};
  15.232 +
  15.233 +#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPTABLE_HPP
    16.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    16.2 +++ b/src/share/vm/gc_implementation/g1/g1StringDedupThread.cpp	Tue Mar 18 19:07:22 2014 +0100
    16.3 @@ -0,0 +1,124 @@
    16.4 +/*
    16.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
    16.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    16.7 + *
    16.8 + * This code is free software; you can redistribute it and/or modify it
    16.9 + * under the terms of the GNU General Public License version 2 only, as
   16.10 + * published by the Free Software Foundation.
   16.11 + *
   16.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
   16.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   16.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   16.15 + * version 2 for more details (a copy is included in the LICENSE file that
   16.16 + * accompanied this code).
   16.17 + *
   16.18 + * You should have received a copy of the GNU General Public License version
   16.19 + * 2 along with this work; if not, write to the Free Software Foundation,
   16.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   16.21 + *
   16.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   16.23 + * or visit www.oracle.com if you need additional information or have any
   16.24 + * questions.
   16.25 + *
   16.26 + */
   16.27 +
   16.28 +#include "precompiled.hpp"
   16.29 +#include "gc_implementation/g1/g1Log.hpp"
   16.30 +#include "gc_implementation/g1/g1StringDedup.hpp"
   16.31 +#include "gc_implementation/g1/g1StringDedupTable.hpp"
   16.32 +#include "gc_implementation/g1/g1StringDedupThread.hpp"
   16.33 +#include "gc_implementation/g1/g1StringDedupQueue.hpp"
   16.34 +
   16.35 +G1StringDedupThread* G1StringDedupThread::_thread = NULL;
   16.36 +
   16.37 +G1StringDedupThread::G1StringDedupThread() :
   16.38 +  ConcurrentGCThread() {
   16.39 +  set_name("String Deduplication Thread");
   16.40 +  create_and_start();
   16.41 +}
   16.42 +
   16.43 +G1StringDedupThread::~G1StringDedupThread() {
   16.44 +  ShouldNotReachHere();
   16.45 +}
   16.46 +
   16.47 +void G1StringDedupThread::create() {
   16.48 +  assert(G1StringDedup::is_enabled(), "String deduplication not enabled");
   16.49 +  assert(_thread == NULL, "One string deduplication thread allowed");
   16.50 +  _thread = new G1StringDedupThread();
   16.51 +}
   16.52 +
   16.53 +G1StringDedupThread* G1StringDedupThread::thread() {
   16.54 +  assert(G1StringDedup::is_enabled(), "String deduplication not enabled");
   16.55 +  assert(_thread != NULL, "String deduplication thread not created");
   16.56 +  return _thread;
   16.57 +}
   16.58 +
   16.59 +void G1StringDedupThread::print_on(outputStream* st) const {
   16.60 +  st->print("\"%s\" ", name());
   16.61 +  Thread::print_on(st);
   16.62 +  st->cr();
   16.63 +}
   16.64 +
   16.65 +void G1StringDedupThread::run() {
   16.66 +  G1StringDedupStat total_stat;
   16.67 +
   16.68 +  initialize_in_thread();
   16.69 +  wait_for_universe_init();
   16.70 +
   16.71 +  // Main loop
   16.72 +  for (;;) {
   16.73 +    G1StringDedupStat stat;
   16.74 +
   16.75 +    stat.mark_idle();
   16.76 +
   16.77 +    // Wait for the queue to become non-empty
   16.78 +    G1StringDedupQueue::wait();
   16.79 +
   16.80 +    // Include this thread in safepoints
   16.81 +    stsJoin();
   16.82 +
   16.83 +    stat.mark_exec();
   16.84 +
   16.85 +    // Process the queue
   16.86 +    for (;;) {
   16.87 +      oop java_string = G1StringDedupQueue::pop();
   16.88 +      if (java_string == NULL) {
   16.89 +        break;
   16.90 +      }
   16.91 +
   16.92 +      G1StringDedupTable::deduplicate(java_string, stat);
   16.93 +
   16.94 +      // Safepoint this thread if needed
   16.95 +      if (stsShouldYield()) {
   16.96 +        stat.mark_block();
   16.97 +        stsYield(NULL);
   16.98 +        stat.mark_unblock();
   16.99 +      }
  16.100 +    }
  16.101 +
  16.102 +    G1StringDedupTable::trim_entry_cache();
  16.103 +
  16.104 +    stat.mark_done();
  16.105 +
  16.106 +    // Print statistics
  16.107 +    total_stat.add(stat);
  16.108 +    print(gclog_or_tty, stat, total_stat);
  16.109 +
  16.110 +    // Exclude this thread from safepoints
  16.111 +    stsLeave();
  16.112 +  }
  16.113 +
  16.114 +  ShouldNotReachHere();
  16.115 +}
  16.116 +
  16.117 +void G1StringDedupThread::print(outputStream* st, const G1StringDedupStat& last_stat, const G1StringDedupStat& total_stat) {
  16.118 +  if (G1Log::fine() || PrintStringDeduplicationStatistics) {
  16.119 +    G1StringDedupStat::print_summary(st, last_stat, total_stat);
  16.120 +    if (PrintStringDeduplicationStatistics) {
  16.121 +      G1StringDedupStat::print_statistics(st, last_stat, false);
  16.122 +      G1StringDedupStat::print_statistics(st, total_stat, true);
  16.123 +      G1StringDedupTable::print_statistics(st);
  16.124 +      G1StringDedupQueue::print_statistics(st);
  16.125 +    }
  16.126 +  }
  16.127 +}
    17.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    17.2 +++ b/src/share/vm/gc_implementation/g1/g1StringDedupThread.hpp	Tue Mar 18 19:07:22 2014 +0100
    17.3 @@ -0,0 +1,56 @@
    17.4 +/*
    17.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
    17.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    17.7 + *
    17.8 + * This code is free software; you can redistribute it and/or modify it
    17.9 + * under the terms of the GNU General Public License version 2 only, as
   17.10 + * published by the Free Software Foundation.
   17.11 + *
   17.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
   17.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   17.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   17.15 + * version 2 for more details (a copy is included in the LICENSE file that
   17.16 + * accompanied this code).
   17.17 + *
   17.18 + * You should have received a copy of the GNU General Public License version
   17.19 + * 2 along with this work; if not, write to the Free Software Foundation,
   17.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   17.21 + *
   17.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   17.23 + * or visit www.oracle.com if you need additional information or have any
   17.24 + * questions.
   17.25 + *
   17.26 + */
   17.27 +
   17.28 +#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPTHREAD_HPP
   17.29 +#define SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPTHREAD_HPP
   17.30 +
   17.31 +#include "gc_implementation/g1/g1StringDedupStat.hpp"
   17.32 +#include "gc_implementation/shared/concurrentGCThread.hpp"
   17.33 +
   17.34 +//
   17.35 +// The deduplication thread is where the actual deduplication occurs. It waits for
   17.36 +// deduplication candidates to appear on the deduplication queue, removes them from
   17.37 +// the queue and tries to deduplicate them. It uses the deduplication hashtable to
   17.38 +// find identical, already existing, character arrays on the heap. The thread runs
   17.39 +// concurrently with the Java application but participates in safepoints to allow
   17.40 +// the GC to adjust and unlink oops from the deduplication queue and table.
   17.41 +//
   17.42 +class G1StringDedupThread: public ConcurrentGCThread {
   17.43 +private:
   17.44 +  static G1StringDedupThread* _thread;
   17.45 +
   17.46 +  G1StringDedupThread();
   17.47 +  ~G1StringDedupThread();
   17.48 +
   17.49 +  void print(outputStream* st, const G1StringDedupStat& last_stat, const G1StringDedupStat& total_stat);
   17.50 +
   17.51 +public:
   17.52 +  static void create();
   17.53 +  static G1StringDedupThread* thread();
   17.54 +
   17.55 +  virtual void run();
   17.56 +  virtual void print_on(outputStream* st) const;
   17.57 +};
   17.58 +
   17.59 +#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPTHREAD_HPP
    18.1 --- a/src/share/vm/gc_implementation/shared/markSweep.inline.hpp	Mon Mar 24 09:14:14 2014 -0700
    18.2 +++ b/src/share/vm/gc_implementation/shared/markSweep.inline.hpp	Tue Mar 18 19:07:22 2014 +0100
    18.3 @@ -1,5 +1,5 @@
    18.4  /*
    18.5 - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
    18.6 + * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.
    18.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    18.8   *
    18.9   * This code is free software; you can redistribute it and/or modify it
   18.10 @@ -30,10 +30,18 @@
   18.11  #include "utilities/stack.inline.hpp"
   18.12  #include "utilities/macros.hpp"
   18.13  #if INCLUDE_ALL_GCS
   18.14 +#include "gc_implementation/g1/g1StringDedup.hpp"
   18.15  #include "gc_implementation/parallelScavenge/psParallelCompact.hpp"
   18.16  #endif // INCLUDE_ALL_GCS
   18.17  
   18.18  inline void MarkSweep::mark_object(oop obj) {
   18.19 +#if INCLUDE_ALL_GCS
   18.20 +  if (G1StringDedup::is_enabled()) {
   18.21 +    // We must enqueue the object before it is marked
   18.22 +    // as we otherwise can't read the object's age.
   18.23 +    G1StringDedup::enqueue_from_mark(obj);
   18.24 +  }
   18.25 +#endif
   18.26    // some marks may contain information we need to preserve so we store them away
   18.27    // and overwrite the mark.  We'll restore it at the end of markSweep.
   18.28    markOop mark = obj->mark();
    19.1 --- a/src/share/vm/runtime/arguments.cpp	Mon Mar 24 09:14:14 2014 -0700
    19.2 +++ b/src/share/vm/runtime/arguments.cpp	Tue Mar 18 19:07:22 2014 +0100
    19.3 @@ -2217,6 +2217,8 @@
    19.4                                         "G1ConcRSHotCardLimit");
    19.5      status = status && verify_interval(G1ConcRSLogCacheSize, 0, 31,
    19.6                                         "G1ConcRSLogCacheSize");
    19.7 +    status = status && verify_interval(StringDeduplicationAgeThreshold, 1, markOopDesc::max_age,
    19.8 +                                       "StringDeduplicationAgeThreshold");
    19.9    }
   19.10    if (UseConcMarkSweepGC) {
   19.11      status = status && verify_min_value(CMSOldPLABNumRefills, 1, "CMSOldPLABNumRefills");
    20.1 --- a/src/share/vm/runtime/globals.hpp	Mon Mar 24 09:14:14 2014 -0700
    20.2 +++ b/src/share/vm/runtime/globals.hpp	Tue Mar 18 19:07:22 2014 +0100
    20.3 @@ -3812,6 +3812,22 @@
    20.4    experimental(uintx, SymbolTableSize, defaultSymbolTableSize,              \
    20.5            "Number of buckets in the JVM internal Symbol table")             \
    20.6                                                                              \
    20.7 +  product(bool, UseStringDeduplication, false,                              \
    20.8 +          "Use string deduplication")                                       \
    20.9 +                                                                            \
   20.10 +  product(bool, PrintStringDeduplicationStatistics, false,                  \
   20.11 +          "Print string deduplication statistics")                          \
   20.12 +                                                                            \
   20.13 +  product(uintx, StringDeduplicationAgeThreshold, 3,                        \
   20.14 +          "A string must reach this age (or be promoted to an old region) " \
   20.15 +          "to be considered for deduplication")                             \
   20.16 +                                                                            \
   20.17 +  diagnostic(bool, StringDeduplicationResizeALot, false,                    \
   20.18 +          "Force table resize every time the table is scanned")             \
   20.19 +                                                                            \
   20.20 +  diagnostic(bool, StringDeduplicationRehashALot, false,                    \
   20.21 +          "Force table rehash every time the table is scanned")             \
   20.22 +                                                                            \
   20.23    develop(bool, TraceDefaultMethods, false,                                 \
   20.24            "Trace the default method processing steps")                      \
   20.25                                                                              \
    21.1 --- a/src/share/vm/runtime/mutexLocker.cpp	Mon Mar 24 09:14:14 2014 -0700
    21.2 +++ b/src/share/vm/runtime/mutexLocker.cpp	Tue Mar 18 19:07:22 2014 +0100
    21.3 @@ -1,5 +1,5 @@
    21.4  /*
    21.5 - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
    21.6 + * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
    21.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    21.8   *
    21.9   * This code is free software; you can redistribute it and/or modify it
   21.10 @@ -58,6 +58,8 @@
   21.11  Mutex*   VtableStubs_lock             = NULL;
   21.12  Mutex*   SymbolTable_lock             = NULL;
   21.13  Mutex*   StringTable_lock             = NULL;
   21.14 +Monitor* StringDedupQueue_lock        = NULL;
   21.15 +Mutex*   StringDedupTable_lock        = NULL;
   21.16  Mutex*   CodeCache_lock               = NULL;
   21.17  Mutex*   MethodData_lock              = NULL;
   21.18  Mutex*   RetData_lock                 = NULL;
   21.19 @@ -196,6 +198,9 @@
   21.20      def(MMUTracker_lock            , Mutex  , leaf     ,   true );
   21.21      def(HotCardCache_lock          , Mutex  , special  ,   true );
   21.22      def(EvacFailureStack_lock      , Mutex  , nonleaf  ,   true );
   21.23 +
   21.24 +    def(StringDedupQueue_lock      , Monitor, leaf,        true );
   21.25 +    def(StringDedupTable_lock      , Mutex  , leaf,        true );
   21.26    }
   21.27    def(ParGCRareEvent_lock          , Mutex  , leaf     ,   true );
   21.28    def(DerivedPointerTableGC_lock   , Mutex,   leaf,        true );
    22.1 --- a/src/share/vm/runtime/mutexLocker.hpp	Mon Mar 24 09:14:14 2014 -0700
    22.2 +++ b/src/share/vm/runtime/mutexLocker.hpp	Tue Mar 18 19:07:22 2014 +0100
    22.3 @@ -1,5 +1,5 @@
    22.4  /*
    22.5 - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
    22.6 + * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
    22.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    22.8   *
    22.9   * This code is free software; you can redistribute it and/or modify it
   22.10 @@ -63,6 +63,8 @@
   22.11  extern Mutex*   VtableStubs_lock;                // a lock on the VtableStubs
   22.12  extern Mutex*   SymbolTable_lock;                // a lock on the symbol table
   22.13  extern Mutex*   StringTable_lock;                // a lock on the interned string table
   22.14 +extern Monitor* StringDedupQueue_lock;           // a lock on the string deduplication queue
   22.15 +extern Mutex*   StringDedupTable_lock;           // a lock on the string deduplication table
   22.16  extern Mutex*   CodeCache_lock;                  // a lock on the CodeCache, rank is special, use MutexLockerEx
   22.17  extern Mutex*   MethodData_lock;                 // a lock on installation of method data
   22.18  extern Mutex*   RetData_lock;                    // a lock on installation of RetData inside method data
    23.1 --- a/test/gc/g1/TestGCLogMessages.java	Mon Mar 24 09:14:14 2014 -0700
    23.2 +++ b/test/gc/g1/TestGCLogMessages.java	Tue Mar 18 19:07:22 2014 +0100
    23.3 @@ -49,11 +49,13 @@
    23.4  
    23.5      output.shouldNotContain("[Redirty Cards");
    23.6      output.shouldNotContain("[Code Root Purge");
    23.7 +    output.shouldNotContain("[String Dedup Fixup");
    23.8      output.shouldNotContain("[Young Free CSet");
    23.9      output.shouldNotContain("[Non-Young Free CSet");
   23.10      output.shouldHaveExitValue(0);
   23.11  
   23.12      pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC",
   23.13 +                                               "-XX:+UseStringDeduplication",
   23.14                                                 "-Xmx10M",
   23.15                                                 "-XX:+PrintGCDetails",
   23.16                                                 GCTest.class.getName());
   23.17 @@ -62,11 +64,13 @@
   23.18  
   23.19      output.shouldContain("[Redirty Cards");
   23.20      output.shouldContain("[Code Root Purge");
   23.21 +    output.shouldContain("[String Dedup Fixup");
   23.22      output.shouldNotContain("[Young Free CSet");
   23.23      output.shouldNotContain("[Non-Young Free CSet");
   23.24      output.shouldHaveExitValue(0);
   23.25  
   23.26      pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC",
   23.27 +                                               "-XX:+UseStringDeduplication",
   23.28                                                 "-Xmx10M",
   23.29                                                 "-XX:+PrintGCDetails",
   23.30                                                 "-XX:+UnlockExperimentalVMOptions",
   23.31 @@ -77,6 +81,7 @@
   23.32  
   23.33      output.shouldContain("[Redirty Cards");
   23.34      output.shouldContain("[Code Root Purge");
   23.35 +    output.shouldContain("[String Dedup Fixup");
   23.36      output.shouldContain("[Young Free CSet");
   23.37      output.shouldContain("[Non-Young Free CSet");
   23.38  
    24.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    24.2 +++ b/test/gc/g1/TestStringDeduplicationAgeThreshold.java	Tue Mar 18 19:07:22 2014 +0100
    24.3 @@ -0,0 +1,36 @@
    24.4 +/*
    24.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
    24.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    24.7 + *
    24.8 + * This code is free software; you can redistribute it and/or modify it
    24.9 + * under the terms of the GNU General Public License version 2 only, as
   24.10 + * published by the Free Software Foundation.
   24.11 + *
   24.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
   24.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   24.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   24.15 + * version 2 for more details (a copy is included in the LICENSE file that
   24.16 + * accompanied this code).
   24.17 + *
   24.18 + * You should have received a copy of the GNU General Public License version
   24.19 + * 2 along with this work; if not, write to the Free Software Foundation,
   24.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   24.21 + *
   24.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   24.23 + * or visit www.oracle.com if you need additional information or have any
   24.24 + * questions.
   24.25 + */
   24.26 +
   24.27 +/*
   24.28 + * @test TestStringDeduplicationAgeThreshold
   24.29 + * @summary Test string deduplication age threshold
   24.30 + * @bug 8029075
   24.31 + * @key gc
   24.32 + * @library /testlibrary
   24.33 + */
   24.34 +
   24.35 +public class TestStringDeduplicationAgeThreshold {
   24.36 +    public static void main(String[] args) throws Exception {
   24.37 +        TestStringDeduplicationTools.testAgeThreshold();
   24.38 +    }
   24.39 +}
    25.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    25.2 +++ b/test/gc/g1/TestStringDeduplicationFullGC.java	Tue Mar 18 19:07:22 2014 +0100
    25.3 @@ -0,0 +1,36 @@
    25.4 +/*
    25.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
    25.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    25.7 + *
    25.8 + * This code is free software; you can redistribute it and/or modify it
    25.9 + * under the terms of the GNU General Public License version 2 only, as
   25.10 + * published by the Free Software Foundation.
   25.11 + *
   25.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
   25.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   25.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   25.15 + * version 2 for more details (a copy is included in the LICENSE file that
   25.16 + * accompanied this code).
   25.17 + *
   25.18 + * You should have received a copy of the GNU General Public License version
   25.19 + * 2 along with this work; if not, write to the Free Software Foundation,
   25.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   25.21 + *
   25.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   25.23 + * or visit www.oracle.com if you need additional information or have any
   25.24 + * questions.
   25.25 + */
   25.26 +
   25.27 +/*
   25.28 + * @test TestStringDeduplicationFullGC
   25.29 + * @summary Test string deduplication during full GC
   25.30 + * @bug 8029075
   25.31 + * @key gc
   25.32 + * @library /testlibrary
   25.33 + */
   25.34 +
   25.35 +public class TestStringDeduplicationFullGC {
   25.36 +    public static void main(String[] args) throws Exception {
   25.37 +        TestStringDeduplicationTools.testFullGC();
   25.38 +    }
   25.39 +}
    26.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    26.2 +++ b/test/gc/g1/TestStringDeduplicationInterned.java	Tue Mar 18 19:07:22 2014 +0100
    26.3 @@ -0,0 +1,36 @@
    26.4 +/*
    26.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
    26.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    26.7 + *
    26.8 + * This code is free software; you can redistribute it and/or modify it
    26.9 + * under the terms of the GNU General Public License version 2 only, as
   26.10 + * published by the Free Software Foundation.
   26.11 + *
   26.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
   26.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   26.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   26.15 + * version 2 for more details (a copy is included in the LICENSE file that
   26.16 + * accompanied this code).
   26.17 + *
   26.18 + * You should have received a copy of the GNU General Public License version
   26.19 + * 2 along with this work; if not, write to the Free Software Foundation,
   26.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   26.21 + *
   26.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   26.23 + * or visit www.oracle.com if you need additional information or have any
   26.24 + * questions.
   26.25 + */
   26.26 +
   26.27 +/*
   26.28 + * @test TestStringDeduplicationInterned
   26.29 + * @summary Test string deduplication of interned strings
   26.30 + * @bug 8029075
   26.31 + * @key gc
   26.32 + * @library /testlibrary
   26.33 + */
   26.34 +
   26.35 +public class TestStringDeduplicationInterned {
   26.36 +    public static void main(String[] args) throws Exception {
   26.37 +        TestStringDeduplicationTools.testInterned();
   26.38 +    }
   26.39 +}
    27.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    27.2 +++ b/test/gc/g1/TestStringDeduplicationMemoryUsage.java	Tue Mar 18 19:07:22 2014 +0100
    27.3 @@ -0,0 +1,36 @@
    27.4 +/*
    27.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
    27.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    27.7 + *
    27.8 + * This code is free software; you can redistribute it and/or modify it
    27.9 + * under the terms of the GNU General Public License version 2 only, as
   27.10 + * published by the Free Software Foundation.
   27.11 + *
   27.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
   27.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   27.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   27.15 + * version 2 for more details (a copy is included in the LICENSE file that
   27.16 + * accompanied this code).
   27.17 + *
   27.18 + * You should have received a copy of the GNU General Public License version
   27.19 + * 2 along with this work; if not, write to the Free Software Foundation,
   27.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   27.21 + *
   27.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   27.23 + * or visit www.oracle.com if you need additional information or have any
   27.24 + * questions.
   27.25 + */
   27.26 +
   27.27 +/*
   27.28 + * @test TestStringDeduplicationMemoryUsage
   27.29 + * @summary Test string deduplication memory usage
   27.30 + * @bug 8029075
   27.31 + * @key gc
   27.32 + * @library /testlibrary
   27.33 + */
   27.34 +
   27.35 +public class TestStringDeduplicationMemoryUsage {
   27.36 +    public static void main(String[] args) throws Exception {
   27.37 +        TestStringDeduplicationTools.testMemoryUsage();
   27.38 +    }
   27.39 +}
    28.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    28.2 +++ b/test/gc/g1/TestStringDeduplicationPrintOptions.java	Tue Mar 18 19:07:22 2014 +0100
    28.3 @@ -0,0 +1,36 @@
    28.4 +/*
    28.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
    28.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    28.7 + *
    28.8 + * This code is free software; you can redistribute it and/or modify it
    28.9 + * under the terms of the GNU General Public License version 2 only, as
   28.10 + * published by the Free Software Foundation.
   28.11 + *
   28.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
   28.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   28.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   28.15 + * version 2 for more details (a copy is included in the LICENSE file that
   28.16 + * accompanied this code).
   28.17 + *
   28.18 + * You should have received a copy of the GNU General Public License version
   28.19 + * 2 along with this work; if not, write to the Free Software Foundation,
   28.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   28.21 + *
   28.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   28.23 + * or visit www.oracle.com if you need additional information or have any
   28.24 + * questions.
   28.25 + */
   28.26 +
   28.27 +/*
   28.28 + * @test TestStringDeduplicationPrintOptions
   28.29 + * @summary Test string deduplication print options
   28.30 + * @bug 8029075
   28.31 + * @key gc
   28.32 + * @library /testlibrary
   28.33 + */
   28.34 +
   28.35 +public class TestStringDeduplicationPrintOptions {
   28.36 +    public static void main(String[] args) throws Exception {
   28.37 +        TestStringDeduplicationTools.testPrintOptions();
   28.38 +    }
   28.39 +}
    29.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    29.2 +++ b/test/gc/g1/TestStringDeduplicationTableRehash.java	Tue Mar 18 19:07:22 2014 +0100
    29.3 @@ -0,0 +1,36 @@
    29.4 +/*
    29.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
    29.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    29.7 + *
    29.8 + * This code is free software; you can redistribute it and/or modify it
    29.9 + * under the terms of the GNU General Public License version 2 only, as
   29.10 + * published by the Free Software Foundation.
   29.11 + *
   29.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
   29.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   29.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   29.15 + * version 2 for more details (a copy is included in the LICENSE file that
   29.16 + * accompanied this code).
   29.17 + *
   29.18 + * You should have received a copy of the GNU General Public License version
   29.19 + * 2 along with this work; if not, write to the Free Software Foundation,
   29.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   29.21 + *
   29.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   29.23 + * or visit www.oracle.com if you need additional information or have any
   29.24 + * questions.
   29.25 + */
   29.26 +
   29.27 +/*
   29.28 + * @test TestStringDeduplicationTableRehash
   29.29 + * @summary Test string deduplication table rehash
   29.30 + * @bug 8029075
   29.31 + * @key gc
   29.32 + * @library /testlibrary
   29.33 + */
   29.34 +
   29.35 +public class TestStringDeduplicationTableRehash {
   29.36 +    public static void main(String[] args) throws Exception {
   29.37 +        TestStringDeduplicationTools.testTableRehash();
   29.38 +    }
   29.39 +}
    30.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    30.2 +++ b/test/gc/g1/TestStringDeduplicationTableResize.java	Tue Mar 18 19:07:22 2014 +0100
    30.3 @@ -0,0 +1,36 @@
    30.4 +/*
    30.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
    30.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    30.7 + *
    30.8 + * This code is free software; you can redistribute it and/or modify it
    30.9 + * under the terms of the GNU General Public License version 2 only, as
   30.10 + * published by the Free Software Foundation.
   30.11 + *
   30.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
   30.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   30.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   30.15 + * version 2 for more details (a copy is included in the LICENSE file that
   30.16 + * accompanied this code).
   30.17 + *
   30.18 + * You should have received a copy of the GNU General Public License version
   30.19 + * 2 along with this work; if not, write to the Free Software Foundation,
   30.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   30.21 + *
   30.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   30.23 + * or visit www.oracle.com if you need additional information or have any
   30.24 + * questions.
   30.25 + */
   30.26 +
   30.27 +/*
   30.28 + * @test TestStringDeduplicationTableResize
   30.29 + * @summary Test string deduplication table resize
   30.30 + * @bug 8029075
   30.31 + * @key gc
   30.32 + * @library /testlibrary
   30.33 + */
   30.34 +
   30.35 +public class TestStringDeduplicationTableResize {
   30.36 +    public static void main(String[] args) throws Exception {
   30.37 +        TestStringDeduplicationTools.testTableResize();
   30.38 +    }
   30.39 +}
    31.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    31.2 +++ b/test/gc/g1/TestStringDeduplicationTools.java	Tue Mar 18 19:07:22 2014 +0100
    31.3 @@ -0,0 +1,512 @@
    31.4 +/*
    31.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
    31.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    31.7 + *
    31.8 + * This code is free software; you can redistribute it and/or modify it
    31.9 + * under the terms of the GNU General Public License version 2 only, as
   31.10 + * published by the Free Software Foundation.
   31.11 + *
   31.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
   31.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   31.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   31.15 + * version 2 for more details (a copy is included in the LICENSE file that
   31.16 + * accompanied this code).
   31.17 + *
   31.18 + * You should have received a copy of the GNU General Public License version
   31.19 + * 2 along with this work; if not, write to the Free Software Foundation,
   31.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   31.21 + *
   31.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   31.23 + * or visit www.oracle.com if you need additional information or have any
   31.24 + * questions.
   31.25 + */
   31.26 +
   31.27 +/*
   31.28 + * Common code for string deduplication tests
   31.29 + */
   31.30 +
   31.31 +import java.lang.management.*;
   31.32 +import java.lang.reflect.*;
   31.33 +import java.security.*;
   31.34 +import java.util.*;
   31.35 +import com.oracle.java.testlibrary.*;
   31.36 +import sun.misc.*;
   31.37 +
   31.38 +class TestStringDeduplicationTools {
   31.39 +    private static final String YoungGC = "YoungGC";
   31.40 +    private static final String FullGC  = "FullGC";
   31.41 +
   31.42 +    private static final int Xmn = 50;  // MB
   31.43 +    private static final int Xms = 100; // MB
   31.44 +    private static final int Xmx = 100; // MB
   31.45 +    private static final int MB = 1024 * 1024;
   31.46 +    private static final int StringLength = 50;
   31.47 +
   31.48 +    private static Field valueField;
   31.49 +    private static Unsafe unsafe;
   31.50 +    private static byte[] dummy;
   31.51 +
   31.52 +    static {
   31.53 +        try {
   31.54 +            Field field = Unsafe.class.getDeclaredField("theUnsafe");
   31.55 +            field.setAccessible(true);
   31.56 +            unsafe = (Unsafe)field.get(null);
   31.57 +
   31.58 +            valueField = String.class.getDeclaredField("value");
   31.59 +            valueField.setAccessible(true);
   31.60 +        } catch (Exception e) {
   31.61 +            throw new RuntimeException(e);
   31.62 +        }
   31.63 +    }
   31.64 +
   31.65 +    private static Object getValue(String string) {
   31.66 +        try {
   31.67 +            return valueField.get(string);
   31.68 +        } catch (Exception e) {
   31.69 +            throw new RuntimeException(e);
   31.70 +        }
   31.71 +    }
   31.72 +
   31.73 +    private static void doFullGc(int numberOfTimes) {
   31.74 +        for (int i = 0; i < numberOfTimes; i++) {
   31.75 +            System.out.println("Begin: Full GC " + (i + 1) + "/" + numberOfTimes);
   31.76 +            System.gc();
   31.77 +            System.out.println("End: Full GC " + (i + 1) + "/" + numberOfTimes);
   31.78 +        }
   31.79 +    }
   31.80 +
   31.81 +    private static void doYoungGc(int numberOfTimes) {
   31.82 +        // Provoke at least numberOfTimes young GCs
   31.83 +        final int objectSize = 128;
   31.84 +        final int maxObjectInYoung = (Xmn * MB) / objectSize;
   31.85 +        for (int i = 0; i < numberOfTimes; i++) {
   31.86 +            System.out.println("Begin: Young GC " + (i + 1) + "/" + numberOfTimes);
   31.87 +            for (int j = 0; j < maxObjectInYoung + 1; j++) {
   31.88 +                dummy = new byte[objectSize];
   31.89 +            }
   31.90 +            System.out.println("End: Young GC " + (i + 1) + "/" + numberOfTimes);
   31.91 +        }
   31.92 +    }
   31.93 +
   31.94 +    private static void forceDeduplication(int ageThreshold, String gcType) {
   31.95 +        // Force deduplication to happen by either causing a FullGC or a YoungGC.
   31.96 +        // We do several collections to also provoke a situation where the the
   31.97 +        // deduplication thread needs to yield while processing the queue. This
   31.98 +        // also tests that the references in the deduplication queue are adjusted
   31.99 +        // accordingly.
  31.100 +        if (gcType.equals(FullGC)) {
  31.101 +            doFullGc(3);
  31.102 +        } else {
  31.103 +            doYoungGc(ageThreshold + 3);
  31.104 +        }
  31.105 +    }
  31.106 +
  31.107 +    private static String generateString(int id) {
  31.108 +        StringBuilder builder = new StringBuilder(StringLength);
  31.109 +
  31.110 +        builder.append("DeduplicationTestString:" + id + ":");
  31.111 +
  31.112 +        while (builder.length() < StringLength) {
  31.113 +            builder.append('X');
  31.114 +        }
  31.115 +
  31.116 +        return builder.toString();
  31.117 +    }
  31.118 +
  31.119 +    private static ArrayList<String> createStrings(int total, int unique) {
  31.120 +        System.out.println("Creating strings: total=" + total + ", unique=" + unique);
  31.121 +        if (total % unique != 0) {
  31.122 +            throw new RuntimeException("Total must be divisible by unique");
  31.123 +        }
  31.124 +
  31.125 +        ArrayList<String> list = new ArrayList<String>(total);
  31.126 +        for (int j = 0; j < total / unique; j++) {
  31.127 +            for (int i = 0; i < unique; i++) {
  31.128 +                list.add(generateString(i));
  31.129 +            }
  31.130 +        }
  31.131 +
  31.132 +        return list;
  31.133 +    }
  31.134 +
  31.135 +    private static void verifyStrings(ArrayList<String> list, int uniqueExpected) {
  31.136 +        for (;;) {
  31.137 +            // Check number of deduplicated strings
  31.138 +            ArrayList<Object> unique = new ArrayList<Object>(uniqueExpected);
  31.139 +            for (String string: list) {
  31.140 +                Object value = getValue(string);
  31.141 +                boolean uniqueValue = true;
  31.142 +                for (Object obj: unique) {
  31.143 +                    if (obj == value) {
  31.144 +                        uniqueValue = false;
  31.145 +                        break;
  31.146 +                    }
  31.147 +                }
  31.148 +
  31.149 +                if (uniqueValue) {
  31.150 +                    unique.add(value);
  31.151 +                }
  31.152 +            }
  31.153 +
  31.154 +            System.out.println("Verifying strings: total=" + list.size() +
  31.155 +                               ", uniqueFound=" + unique.size() +
  31.156 +                               ", uniqueExpected=" + uniqueExpected);
  31.157 +
  31.158 +            if (unique.size() == uniqueExpected) {
  31.159 +                System.out.println("Deduplication completed");
  31.160 +                break;
  31.161 +            } else {
  31.162 +                System.out.println("Deduplication not completed, waiting...");
  31.163 +
  31.164 +                // Give the deduplication thread time to complete
  31.165 +                try {
  31.166 +                    Thread.sleep(1000);
  31.167 +                } catch (Exception e) {
  31.168 +                    throw new RuntimeException(e);
  31.169 +                }
  31.170 +            }
  31.171 +        }
  31.172 +    }
  31.173 +
  31.174 +    private static OutputAnalyzer runTest(String... extraArgs) throws Exception {
  31.175 +        String[] defaultArgs = new String[] {
  31.176 +            "-Xmn" + Xmn + "m",
  31.177 +            "-Xms" + Xms + "m",
  31.178 +            "-Xmx" + Xmx + "m",
  31.179 +            "-XX:+UseG1GC",
  31.180 +            "-XX:+UnlockDiagnosticVMOptions",
  31.181 +            "-XX:+VerifyAfterGC" // Always verify after GC
  31.182 +        };
  31.183 +
  31.184 +        ArrayList<String> args = new ArrayList<String>();
  31.185 +        args.addAll(Arrays.asList(defaultArgs));
  31.186 +        args.addAll(Arrays.asList(extraArgs));
  31.187 +
  31.188 +        ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(args.toArray(new String[args.size()]));
  31.189 +        OutputAnalyzer output = new OutputAnalyzer(pb.start());
  31.190 +        System.err.println(output.getStderr());
  31.191 +        System.out.println(output.getStdout());
  31.192 +        return output;
  31.193 +    }
  31.194 +
  31.195 +    private static class DeduplicationTest {
  31.196 +        public static void main(String[] args) {
  31.197 +            System.out.println("Begin: DeduplicationTest");
  31.198 +
  31.199 +            final int numberOfStrings = Integer.parseUnsignedInt(args[0]);
  31.200 +            final int numberOfUniqueStrings = Integer.parseUnsignedInt(args[1]);
  31.201 +            final int ageThreshold = Integer.parseUnsignedInt(args[2]);
  31.202 +            final String gcType = args[3];
  31.203 +
  31.204 +            ArrayList<String> list = createStrings(numberOfStrings, numberOfUniqueStrings);
  31.205 +            forceDeduplication(ageThreshold, gcType);
  31.206 +            verifyStrings(list, numberOfUniqueStrings);
  31.207 +
  31.208 +            System.out.println("End: DeduplicationTest");
  31.209 +        }
  31.210 +
  31.211 +        public static OutputAnalyzer run(int numberOfStrings, int ageThreshold, String gcType, String... extraArgs) throws Exception {
  31.212 +            String[] defaultArgs = new String[] {
  31.213 +                "-XX:+UseStringDeduplication",
  31.214 +                "-XX:StringDeduplicationAgeThreshold=" + ageThreshold,
  31.215 +                DeduplicationTest.class.getName(),
  31.216 +                "" + numberOfStrings,
  31.217 +                "" + numberOfStrings / 2,
  31.218 +                "" + ageThreshold,
  31.219 +                gcType
  31.220 +            };
  31.221 +
  31.222 +            ArrayList<String> args = new ArrayList<String>();
  31.223 +            args.addAll(Arrays.asList(extraArgs));
  31.224 +            args.addAll(Arrays.asList(defaultArgs));
  31.225 +
  31.226 +            return runTest(args.toArray(new String[args.size()]));
  31.227 +        }
  31.228 +    }
  31.229 +
  31.230 +    private static class InternedTest {
  31.231 +        public static void main(String[] args) {
  31.232 +            // This test verifies that interned strings are always
  31.233 +            // deduplicated when being interned, and never after
  31.234 +            // being interned.
  31.235 +
  31.236 +            System.out.println("Begin: InternedTest");
  31.237 +
  31.238 +            final int ageThreshold = Integer.parseUnsignedInt(args[0]);
  31.239 +            final String baseString = "DeduplicationTestString:" + InternedTest.class.getName();
  31.240 +
  31.241 +            // Create duplicate of baseString
  31.242 +            StringBuilder sb1 = new StringBuilder(baseString);
  31.243 +            String dupString1 = sb1.toString();
  31.244 +            if (getValue(dupString1) == getValue(baseString)) {
  31.245 +                throw new RuntimeException("Values should not match");
  31.246 +            }
  31.247 +
  31.248 +            // Force baseString to be inspected for deduplication
  31.249 +            // and be inserted into the deduplication hashtable.
  31.250 +            forceDeduplication(ageThreshold, FullGC);
  31.251 +
  31.252 +            // Wait for deduplication to occur
  31.253 +            while (getValue(dupString1) != getValue(baseString)) {
  31.254 +                System.out.println("Waiting...");
  31.255 +                try {
  31.256 +                    Thread.sleep(100);
  31.257 +                } catch (Exception e) {
  31.258 +                    throw new RuntimeException(e);
  31.259 +                }
  31.260 +            }
  31.261 +
  31.262 +            // Create a new duplicate of baseString
  31.263 +            StringBuilder sb2 = new StringBuilder(baseString);
  31.264 +            String dupString2 = sb2.toString();
  31.265 +            if (getValue(dupString2) == getValue(baseString)) {
  31.266 +                throw new RuntimeException("Values should not match");
  31.267 +            }
  31.268 +
  31.269 +            // Intern the new duplicate
  31.270 +            Object beforeInternedValue = getValue(dupString2);
  31.271 +            String internedString = dupString2.intern();
  31.272 +            if (internedString != dupString2) {
  31.273 +                throw new RuntimeException("String should match");
  31.274 +            }
  31.275 +            if (getValue(internedString) != getValue(baseString)) {
  31.276 +                throw new RuntimeException("Values should match");
  31.277 +            }
  31.278 +
  31.279 +            // Check original value of interned string, to make sure
  31.280 +            // deduplication happened on the interned string and not
  31.281 +            // on the base string
  31.282 +            if (beforeInternedValue == getValue(baseString)) {
  31.283 +                throw new RuntimeException("Values should not match");
  31.284 +            }
  31.285 +
  31.286 +            System.out.println("End: InternedTest");
  31.287 +        }
  31.288 +
  31.289 +        public static OutputAnalyzer run() throws Exception {
  31.290 +            return runTest("-XX:+PrintGC",
  31.291 +                           "-XX:+PrintGCDetails",
  31.292 +                           "-XX:+UseStringDeduplication",
  31.293 +                           "-XX:+PrintStringDeduplicationStatistics",
  31.294 +                           "-XX:StringDeduplicationAgeThreshold=" + DefaultAgeThreshold,
  31.295 +                           InternedTest.class.getName(),
  31.296 +                           "" + DefaultAgeThreshold);
  31.297 +        }
  31.298 +    }
  31.299 +
  31.300 +    private static class MemoryUsageTest {
  31.301 +        public static void main(String[] args) {
  31.302 +            System.out.println("Begin: MemoryUsageTest");
  31.303 +
  31.304 +            final boolean useStringDeduplication = Boolean.parseBoolean(args[0]);
  31.305 +            final int numberOfStrings = LargeNumberOfStrings;
  31.306 +            final int numberOfUniqueStrings = 1;
  31.307 +
  31.308 +            ArrayList<String> list = createStrings(numberOfStrings, numberOfUniqueStrings);
  31.309 +            forceDeduplication(DefaultAgeThreshold, FullGC);
  31.310 +
  31.311 +            if (useStringDeduplication) {
  31.312 +                verifyStrings(list, numberOfUniqueStrings);
  31.313 +            }
  31.314 +
  31.315 +            System.gc();
  31.316 +            System.out.println("Heap Memory Usage: " + ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed());
  31.317 +
  31.318 +            System.out.println("End: MemoryUsageTest");
  31.319 +        }
  31.320 +
  31.321 +        public static OutputAnalyzer run(boolean useStringDeduplication) throws Exception {
  31.322 +            String[] extraArgs = new String[0];
  31.323 +
  31.324 +            if (useStringDeduplication) {
  31.325 +                extraArgs = new String[] {
  31.326 +                    "-XX:+UseStringDeduplication",
  31.327 +                    "-XX:+PrintStringDeduplicationStatistics",
  31.328 +                    "-XX:StringDeduplicationAgeThreshold=" + DefaultAgeThreshold
  31.329 +                };
  31.330 +            }
  31.331 +
  31.332 +            String[] defaultArgs = new String[] {
  31.333 +                "-XX:+PrintGC",
  31.334 +                "-XX:+PrintGCDetails",
  31.335 +                MemoryUsageTest.class.getName(),
  31.336 +                "" + useStringDeduplication
  31.337 +            };
  31.338 +
  31.339 +            ArrayList<String> args = new ArrayList<String>();
  31.340 +            args.addAll(Arrays.asList(extraArgs));
  31.341 +            args.addAll(Arrays.asList(defaultArgs));
  31.342 +
  31.343 +            return runTest(args.toArray(new String[args.size()]));
  31.344 +        }
  31.345 +    }
  31.346 +
  31.347 +    /*
  31.348 +     * Tests
  31.349 +     */
  31.350 +
  31.351 +    private static final int LargeNumberOfStrings = 10000;
  31.352 +    private static final int SmallNumberOfStrings = 10;
  31.353 +
  31.354 +    private static final int MaxAgeThreshold      = 15;
  31.355 +    private static final int DefaultAgeThreshold  = 3;
  31.356 +    private static final int MinAgeThreshold      = 1;
  31.357 +
  31.358 +    private static final int TooLowAgeThreshold   = MinAgeThreshold - 1;
  31.359 +    private static final int TooHighAgeThreshold  = MaxAgeThreshold + 1;
  31.360 +
  31.361 +    public static void testYoungGC() throws Exception {
  31.362 +        // Do young GC to age strings to provoke deduplication
  31.363 +        OutputAnalyzer output = DeduplicationTest.run(LargeNumberOfStrings,
  31.364 +                                                      DefaultAgeThreshold,
  31.365 +                                                      YoungGC,
  31.366 +                                                      "-XX:+PrintGC",
  31.367 +                                                      "-XX:+PrintStringDeduplicationStatistics");
  31.368 +        output.shouldNotContain("Full GC");
  31.369 +        output.shouldContain("GC pause (G1 Evacuation Pause) (young)");
  31.370 +        output.shouldContain("GC concurrent-string-deduplication");
  31.371 +        output.shouldContain("Deduplicated:");
  31.372 +        output.shouldHaveExitValue(0);
  31.373 +    }
  31.374 +
  31.375 +    public static void testFullGC() throws Exception {
  31.376 +        // Do full GC to age strings to provoke deduplication
  31.377 +        OutputAnalyzer output = DeduplicationTest.run(LargeNumberOfStrings,
  31.378 +                                                      DefaultAgeThreshold,
  31.379 +                                                      FullGC,
  31.380 +                                                      "-XX:+PrintGC",
  31.381 +                                                      "-XX:+PrintStringDeduplicationStatistics");
  31.382 +        output.shouldNotContain("GC pause (G1 Evacuation Pause) (young)");
  31.383 +        output.shouldContain("Full GC");
  31.384 +        output.shouldContain("GC concurrent-string-deduplication");
  31.385 +        output.shouldContain("Deduplicated:");
  31.386 +        output.shouldHaveExitValue(0);
  31.387 +    }
  31.388 +
  31.389 +    public static void testTableResize() throws Exception {
  31.390 +        // Test with StringDeduplicationResizeALot
  31.391 +        OutputAnalyzer output = DeduplicationTest.run(LargeNumberOfStrings,
  31.392 +                                                      DefaultAgeThreshold,
  31.393 +                                                      YoungGC,
  31.394 +                                                      "-XX:+PrintGC",
  31.395 +                                                      "-XX:+PrintStringDeduplicationStatistics",
  31.396 +                                                      "-XX:+StringDeduplicationResizeALot");
  31.397 +        output.shouldContain("GC concurrent-string-deduplication");
  31.398 +        output.shouldContain("Deduplicated:");
  31.399 +        output.shouldNotContain("Resize Count: 0");
  31.400 +        output.shouldHaveExitValue(0);
  31.401 +    }
  31.402 +
  31.403 +    public static void testTableRehash() throws Exception {
  31.404 +        // Test with StringDeduplicationRehashALot
  31.405 +        OutputAnalyzer output = DeduplicationTest.run(LargeNumberOfStrings,
  31.406 +                                                      DefaultAgeThreshold,
  31.407 +                                                      YoungGC,
  31.408 +                                                      "-XX:+PrintGC",
  31.409 +                                                      "-XX:+PrintStringDeduplicationStatistics",
  31.410 +                                                      "-XX:+StringDeduplicationRehashALot");
  31.411 +        output.shouldContain("GC concurrent-string-deduplication");
  31.412 +        output.shouldContain("Deduplicated:");
  31.413 +        output.shouldNotContain("Rehash Count: 0");
  31.414 +        output.shouldNotContain("Hash Seed: 0x0");
  31.415 +        output.shouldHaveExitValue(0);
  31.416 +    }
  31.417 +
  31.418 +    public static void testAgeThreshold() throws Exception {
  31.419 +        OutputAnalyzer output;
  31.420 +
  31.421 +        // Test with max age theshold
  31.422 +        output = DeduplicationTest.run(SmallNumberOfStrings,
  31.423 +                                       MaxAgeThreshold,
  31.424 +                                       YoungGC,
  31.425 +                                       "-XX:+PrintGC",
  31.426 +                                       "-XX:+PrintStringDeduplicationStatistics");
  31.427 +        output.shouldContain("GC concurrent-string-deduplication");
  31.428 +        output.shouldContain("Deduplicated:");
  31.429 +        output.shouldHaveExitValue(0);
  31.430 +
  31.431 +        // Test with min age theshold
  31.432 +        output = DeduplicationTest.run(SmallNumberOfStrings,
  31.433 +                                       MinAgeThreshold,
  31.434 +                                       YoungGC,
  31.435 +                                       "-XX:+PrintGC",
  31.436 +                                       "-XX:+PrintStringDeduplicationStatistics");
  31.437 +        output.shouldContain("GC concurrent-string-deduplication");
  31.438 +        output.shouldContain("Deduplicated:");
  31.439 +        output.shouldHaveExitValue(0);
  31.440 +
  31.441 +        // Test with too low age threshold
  31.442 +        output = DeduplicationTest.run(SmallNumberOfStrings,
  31.443 +                                       TooLowAgeThreshold,
  31.444 +                                       YoungGC);
  31.445 +        output.shouldContain("StringDeduplicationAgeThreshold of " + TooLowAgeThreshold +
  31.446 +                             " is invalid; must be between " + MinAgeThreshold + " and " + MaxAgeThreshold);
  31.447 +        output.shouldHaveExitValue(1);
  31.448 +
  31.449 +        // Test with too high age threshold
  31.450 +        output = DeduplicationTest.run(SmallNumberOfStrings,
  31.451 +                                       TooHighAgeThreshold,
  31.452 +                                       YoungGC);
  31.453 +        output.shouldContain("StringDeduplicationAgeThreshold of " + TooHighAgeThreshold +
  31.454 +                             " is invalid; must be between " + MinAgeThreshold + " and " + MaxAgeThreshold);
  31.455 +        output.shouldHaveExitValue(1);
  31.456 +    }
  31.457 +
  31.458 +    public static void testPrintOptions() throws Exception {
  31.459 +        OutputAnalyzer output;
  31.460 +
  31.461 +        // Test without PrintGC and without PrintStringDeduplicationStatistics
  31.462 +        output = DeduplicationTest.run(SmallNumberOfStrings,
  31.463 +                                       DefaultAgeThreshold,
  31.464 +                                       YoungGC);
  31.465 +        output.shouldNotContain("GC concurrent-string-deduplication");
  31.466 +        output.shouldNotContain("Deduplicated:");
  31.467 +        output.shouldHaveExitValue(0);
  31.468 +
  31.469 +        // Test with PrintGC but without PrintStringDeduplicationStatistics
  31.470 +        output = DeduplicationTest.run(SmallNumberOfStrings,
  31.471 +                                       DefaultAgeThreshold,
  31.472 +                                       YoungGC,
  31.473 +                                       "-XX:+PrintGC");
  31.474 +        output.shouldContain("GC concurrent-string-deduplication");
  31.475 +        output.shouldNotContain("Deduplicated:");
  31.476 +        output.shouldHaveExitValue(0);
  31.477 +    }
  31.478 +
  31.479 +    public static void testInterned() throws Exception {
  31.480 +        // Test that interned strings are deduplicated before being interned
  31.481 +        OutputAnalyzer output = InternedTest.run();
  31.482 +        output.shouldHaveExitValue(0);
  31.483 +    }
  31.484 +
  31.485 +    public static void testMemoryUsage() throws Exception {
  31.486 +        // Test that memory usage is reduced after deduplication
  31.487 +        OutputAnalyzer output;
  31.488 +        final String usagePattern = "Heap Memory Usage: (\\d+)";
  31.489 +
  31.490 +        // Run without deduplication
  31.491 +        output = MemoryUsageTest.run(false);
  31.492 +        output.shouldHaveExitValue(0);
  31.493 +        final long memoryUsageWithoutDedup = Long.parseLong(output.firstMatch(usagePattern, 1));
  31.494 +
  31.495 +        // Run with deduplication
  31.496 +        output = MemoryUsageTest.run(true);
  31.497 +        output.shouldHaveExitValue(0);
  31.498 +        final long memoryUsageWithDedup = Long.parseLong(output.firstMatch(usagePattern, 1));
  31.499 +
  31.500 +        // Calculate expected memory usage with deduplication enabled. This calculation does
  31.501 +        // not take alignment and padding into account, so it's a conservative estimate.
  31.502 +        final long sizeOfChar = 2; // bytes
  31.503 +        final long bytesSaved = (LargeNumberOfStrings - 1) * (StringLength * sizeOfChar + unsafe.ARRAY_CHAR_BASE_OFFSET);
  31.504 +        final long memoryUsageWithDedupExpected = memoryUsageWithoutDedup - bytesSaved;
  31.505 +
  31.506 +        System.out.println("Memory usage summary:");
  31.507 +        System.out.println("   memoryUsageWithoutDedup:      " + memoryUsageWithoutDedup);
  31.508 +        System.out.println("   memoryUsageWithDedup:         " + memoryUsageWithDedup);
  31.509 +        System.out.println("   memoryUsageWithDedupExpected: " + memoryUsageWithDedupExpected);
  31.510 +
  31.511 +        if (memoryUsageWithDedup > memoryUsageWithDedupExpected) {
  31.512 +            throw new Exception("Unexpected memory usage, memoryUsageWithDedup should less or equal to memoryUsageWithDedupExpected");
  31.513 +        }
  31.514 +    }
  31.515 +}
    32.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    32.2 +++ b/test/gc/g1/TestStringDeduplicationYoungGC.java	Tue Mar 18 19:07:22 2014 +0100
    32.3 @@ -0,0 +1,36 @@
    32.4 +/*
    32.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
    32.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    32.7 + *
    32.8 + * This code is free software; you can redistribute it and/or modify it
    32.9 + * under the terms of the GNU General Public License version 2 only, as
   32.10 + * published by the Free Software Foundation.
   32.11 + *
   32.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
   32.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   32.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   32.15 + * version 2 for more details (a copy is included in the LICENSE file that
   32.16 + * accompanied this code).
   32.17 + *
   32.18 + * You should have received a copy of the GNU General Public License version
   32.19 + * 2 along with this work; if not, write to the Free Software Foundation,
   32.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   32.21 + *
   32.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   32.23 + * or visit www.oracle.com if you need additional information or have any
   32.24 + * questions.
   32.25 + */
   32.26 +
   32.27 +/*
   32.28 + * @test TestStringDeduplicationYoungGC
   32.29 + * @summary Test string deduplication during young GC
   32.30 + * @bug 8029075
   32.31 + * @key gc
   32.32 + * @library /testlibrary
   32.33 + */
   32.34 +
   32.35 +public class TestStringDeduplicationYoungGC {
   32.36 +    public static void main(String[] args) throws Exception {
   32.37 +        TestStringDeduplicationTools.testYoungGC();
   32.38 +    }
   32.39 +}

mercurial