Skip to content
Permalink
Browse files
8267186: Add string deduplication support to ZGC
Reviewed-by: eosterlund, kbarrett, stefank
  • Loading branch information
pliden committed Aug 11, 2021
1 parent 0d0f2d0 commit abebbe2335a6dc9b12e5f271bf32cdc54f80b660
Showing 11 changed files with 246 additions and 36 deletions.
@@ -116,7 +116,7 @@ size_t StringDedup::Config::desired_table_size(size_t entry_count) {
bool StringDedup::Config::ergo_initialize() {
if (!UseStringDeduplication) {
return true;
} else if (!UseG1GC && !UseShenandoahGC) {
} else if (!UseG1GC && !UseShenandoahGC && !UseZGC) {
// String deduplication requested but not supported by the selected GC.
// Warn and force disable, but don't error except in debug build with
// incorrect default.
@@ -24,15 +24,18 @@
#include "precompiled.hpp"
#include "classfile/classLoaderData.hpp"
#include "classfile/classLoaderDataGraph.hpp"
#include "classfile/javaClasses.inline.hpp"
#include "code/nmethod.hpp"
#include "gc/shared/gc_globals.hpp"
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "gc/shared/suspendibleThreadSet.hpp"
#include "gc/z/zAbort.inline.hpp"
#include "gc/z/zBarrier.inline.hpp"
#include "gc/z/zHeap.inline.hpp"
#include "gc/z/zLock.inline.hpp"
#include "gc/z/zMark.inline.hpp"
#include "gc/z/zMarkCache.inline.hpp"
#include "gc/z/zMarkContext.inline.hpp"
#include "gc/z/zMarkStack.inline.hpp"
#include "gc/z/zMarkTerminate.inline.hpp"
#include "gc/z/zNMethod.hpp"
@@ -279,7 +282,27 @@ void ZMark::follow_object(oop obj, bool finalizable) {
}
}

void ZMark::mark_and_follow(ZMarkCache* cache, ZMarkStackEntry entry) {
static void try_deduplicate(ZMarkContext* context, oop obj) {
if (!StringDedup::is_enabled()) {
// Not enabled
return;
}

if (!java_lang_String::is_instance_inlined(obj)) {
// Not a String object
return;
}

if (java_lang_String::test_and_set_deduplication_requested(obj)) {
// Already requested deduplication
return;
}

// Request deduplication
context->string_dedup_requests()->add(obj);
}

void ZMark::mark_and_follow(ZMarkContext* context, ZMarkStackEntry entry) {
// Decode flags
const bool finalizable = entry.finalizable();
const bool partial_array = entry.partial_array();
@@ -311,26 +334,32 @@ void ZMark::mark_and_follow(ZMarkCache* cache, ZMarkStackEntry entry) {
// and alignment paddings can never be reclaimed.
const size_t size = ZUtils::object_size(addr);
const size_t aligned_size = align_up(size, page->object_alignment());
cache->inc_live(page, aligned_size);
context->cache()->inc_live(page, aligned_size);
}

// Follow
if (follow) {
if (is_array(addr)) {
follow_array_object(objArrayOop(ZOop::from_address(addr)), finalizable);
} else {
follow_object(ZOop::from_address(addr), finalizable);
const oop obj = ZOop::from_address(addr);
follow_object(obj, finalizable);

// Try deduplicate
try_deduplicate(context, obj);
}
}
}

template <typename T>
bool ZMark::drain(ZMarkStripe* stripe, ZMarkThreadLocalStacks* stacks, ZMarkCache* cache, T* timeout) {
bool ZMark::drain(ZMarkContext* context, T* timeout) {
ZMarkStripe* const stripe = context->stripe();
ZMarkThreadLocalStacks* const stacks = context->stacks();
ZMarkStackEntry entry;

// Drain stripe stacks
while (stacks->pop(&_allocator, &_stripes, stripe, entry)) {
mark_and_follow(cache, entry);
mark_and_follow(context, entry);

// Check timeout
if (timeout->has_expired()) {
@@ -343,7 +372,10 @@ bool ZMark::drain(ZMarkStripe* stripe, ZMarkThreadLocalStacks* stacks, ZMarkCach
return !timeout->has_expired();
}

bool ZMark::try_steal_local(ZMarkStripe* stripe, ZMarkThreadLocalStacks* stacks) {
bool ZMark::try_steal_local(ZMarkContext* context) {
ZMarkStripe* const stripe = context->stripe();
ZMarkThreadLocalStacks* const stacks = context->stacks();

// Try to steal a local stack from another stripe
for (ZMarkStripe* victim_stripe = _stripes.stripe_next(stripe);
victim_stripe != stripe;
@@ -360,7 +392,10 @@ bool ZMark::try_steal_local(ZMarkStripe* stripe, ZMarkThreadLocalStacks* stacks)
return false;
}

bool ZMark::try_steal_global(ZMarkStripe* stripe, ZMarkThreadLocalStacks* stacks) {
bool ZMark::try_steal_global(ZMarkContext* context) {
ZMarkStripe* const stripe = context->stripe();
ZMarkThreadLocalStacks* const stacks = context->stacks();

// Try to steal a stack from another stripe
for (ZMarkStripe* victim_stripe = _stripes.stripe_next(stripe);
victim_stripe != stripe;
@@ -377,8 +412,8 @@ bool ZMark::try_steal_global(ZMarkStripe* stripe, ZMarkThreadLocalStacks* stacks
return false;
}

bool ZMark::try_steal(ZMarkStripe* stripe, ZMarkThreadLocalStacks* stacks) {
return try_steal_local(stripe, stacks) || try_steal_global(stripe, stacks);
bool ZMark::try_steal(ZMarkContext* context) {
return try_steal_local(context) || try_steal_global(context);
}

void ZMark::idle() const {
@@ -496,17 +531,17 @@ class ZMarkNoTimeout : public StackObj {
}
};

void ZMark::work_without_timeout(ZMarkCache* cache, ZMarkStripe* stripe, ZMarkThreadLocalStacks* stacks) {
void ZMark::work_without_timeout(ZMarkContext* context) {
ZStatTimer timer(ZSubPhaseConcurrentMark);
ZMarkNoTimeout no_timeout;

for (;;) {
if (!drain(stripe, stacks, cache, &no_timeout)) {
if (!drain(context, &no_timeout)) {
// Abort
break;
}

if (try_steal(stripe, stacks)) {
if (try_steal(context)) {
// Stole work
continue;
}
@@ -561,17 +596,17 @@ class ZMarkTimeout : public StackObj {
}
};

void ZMark::work_with_timeout(ZMarkCache* cache, ZMarkStripe* stripe, ZMarkThreadLocalStacks* stacks, uint64_t timeout_in_micros) {
void ZMark::work_with_timeout(ZMarkContext* context, uint64_t timeout_in_micros) {
ZStatTimer timer(ZSubPhaseMarkTryComplete);
ZMarkTimeout timeout(timeout_in_micros);

for (;;) {
if (!drain(stripe, stacks, cache, &timeout)) {
if (!drain(context, &timeout)) {
// Timed out
break;
}

if (try_steal(stripe, stacks)) {
if (try_steal(context)) {
// Stole work
continue;
}
@@ -582,14 +617,14 @@ void ZMark::work_with_timeout(ZMarkCache* cache, ZMarkStripe* stripe, ZMarkThrea
}

void ZMark::work(uint64_t timeout_in_micros) {
ZMarkCache cache(_stripes.nstripes());
ZMarkStripe* const stripe = _stripes.stripe_for_worker(_nworkers, ZThread::worker_id());
ZMarkThreadLocalStacks* const stacks = ZThreadLocalData::stacks(Thread::current());
ZMarkContext context(_stripes.nstripes(), stripe, stacks);

if (timeout_in_micros == 0) {
work_without_timeout(&cache, stripe, stacks);
work_without_timeout(&context);
} else {
work_with_timeout(&cache, stripe, stacks, timeout_in_micros);
work_with_timeout(&context, timeout_in_micros);
}

// Flush and publish stacks
@@ -32,7 +32,7 @@
#include "utilities/globalDefinitions.hpp"

class Thread;
class ZMarkCache;
class ZMarkContext;
class ZPageTable;
class ZWorkers;

@@ -64,15 +64,12 @@ class ZMark {
void follow_partial_array(ZMarkStackEntry entry, bool finalizable);
void follow_array_object(objArrayOop obj, bool finalizable);
void follow_object(oop obj, bool finalizable);
void mark_and_follow(ZMarkCache* cache, ZMarkStackEntry entry);

template <typename T> bool drain(ZMarkStripe* stripe,
ZMarkThreadLocalStacks* stacks,
ZMarkCache* cache,
T* timeout);
bool try_steal_local(ZMarkStripe* stripe, ZMarkThreadLocalStacks* stacks);
bool try_steal_global(ZMarkStripe* stripe, ZMarkThreadLocalStacks* stacks);
bool try_steal(ZMarkStripe* stripe, ZMarkThreadLocalStacks* stacks);
void mark_and_follow(ZMarkContext* context, ZMarkStackEntry entry);

template <typename T> bool drain(ZMarkContext* context, T* timeout);
bool try_steal_local(ZMarkContext* context);
bool try_steal_global(ZMarkContext* context);
bool try_steal(ZMarkContext* context);
void idle() const;
bool flush(bool at_safepoint);
bool try_proactive_flush();
@@ -84,13 +81,8 @@ class ZMark {
void prepare_work();
void finish_work();

void work_without_timeout(ZMarkCache* cache,
ZMarkStripe* stripe,
ZMarkThreadLocalStacks* stacks);
void work_with_timeout(ZMarkCache* cache,
ZMarkStripe* stripe,
ZMarkThreadLocalStacks* stacks,
uint64_t timeout_in_micros);
void work_without_timeout(ZMarkContext* context);
void work_with_timeout(ZMarkContext* context, uint64_t timeout_in_micros);
void work(uint64_t timeout_in_micros);

void verify_all_stacks_empty() const;
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

#ifndef SHARE_GC_Z_ZMARKCONTEXT_HPP
#define SHARE_GC_Z_ZMARKCONTEXT_HPP

#include "gc/z/zMarkCache.hpp"
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "memory/allocation.hpp"

class ZMarkStripe;
class ZMarkThreadLocalStacks;

class ZMarkContext : public StackObj {
private:
ZMarkCache _cache;
ZMarkStripe* const _stripe;
ZMarkThreadLocalStacks* const _stacks;
StringDedup::Requests _string_dedup_requests;

public:
ZMarkContext(size_t nstripes,
ZMarkStripe* stripe,
ZMarkThreadLocalStacks* stacks);

ZMarkCache* cache();
ZMarkStripe* stripe();
ZMarkThreadLocalStacks* stacks();
StringDedup::Requests* string_dedup_requests();
};

#endif // SHARE_GC_Z_ZMARKCONTEXT_HPP
@@ -0,0 +1,53 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

#ifndef SHARE_GC_Z_ZMARKCONTEXT_INLINE_HPP
#define SHARE_GC_Z_ZMARKCONTEXT_INLINE_HPP

#include "gc/z/zMarkContext.hpp"

inline ZMarkContext::ZMarkContext(size_t nstripes,
ZMarkStripe* stripe,
ZMarkThreadLocalStacks* stacks) :
_cache(nstripes),
_stripe(stripe),
_stacks(stacks),
_string_dedup_requests() {}

inline ZMarkCache* ZMarkContext::cache() {
return &_cache;
}

inline ZMarkStripe* ZMarkContext::stripe() {
return _stripe;
}

inline ZMarkThreadLocalStacks* ZMarkContext::stacks() {
return _stacks;
}

inline StringDedup::Requests* ZMarkContext::string_dedup_requests() {
return &_string_dedup_requests;
}

#endif // SHARE_GC_Z_ZMARKCACHE_INLINE_HPP
@@ -49,6 +49,19 @@
* @run driver gc.stringdedup.TestStringDeduplicationAgeThreshold Shenandoah
*/

/*
* @test TestStringDeduplicationAgeThreshold
* @summary Test string deduplication age threshold
* @bug 8029075
* @requires vm.gc.Z
* @library /test/lib
* @library /
* @modules java.base/jdk.internal.misc:open
* @modules java.base/java.lang:open
* java.management
* @run driver gc.stringdedup.TestStringDeduplicationAgeThreshold Z
*/

public class TestStringDeduplicationAgeThreshold {
public static void main(String[] args) throws Exception {
TestStringDeduplicationTools.selectGC(args);
@@ -49,6 +49,19 @@
* @run driver gc.stringdedup.TestStringDeduplicationFullGC Shenandoah
*/

/*
* @test TestStringDeduplicationFullGC
* @summary Test string deduplication during full GC
* @bug 8029075
* @requires vm.gc.Z
* @library /test/lib
* @library /
* @modules java.base/jdk.internal.misc:open
* @modules java.base/java.lang:open
* java.management
* @run driver gc.stringdedup.TestStringDeduplicationFullGC Z
*/

public class TestStringDeduplicationFullGC {
public static void main(String[] args) throws Exception {
TestStringDeduplicationTools.selectGC(args);

1 comment on commit abebbe2

@openjdk-notifier
Copy link

@openjdk-notifier openjdk-notifier bot commented on abebbe2 Aug 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.