8206424: Use locking for cleaning ProtectionDomainTable

ServiceThread is now in charge of cleaning ProtectionDomainTable entries

Reviewed-by: coleenp, iklam
This commit is contained in:
Patricio Chilano Mateo 2018-08-31 10:22:04 -04:00
parent 98242123a3
commit 16b92a561c
8 changed files with 141 additions and 5 deletions

View File

@ -44,11 +44,19 @@ int ProtectionDomainCacheTable::index_for(Handle protection_domain) {
ProtectionDomainCacheTable::ProtectionDomainCacheTable(int table_size)
: Hashtable<ClassLoaderWeakHandle, mtClass>(table_size, sizeof(ProtectionDomainCacheEntry))
{
{ _dead_entries = false;
_total_oops_removed = 0;
}
void ProtectionDomainCacheTable::trigger_cleanup() {
MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag);
_dead_entries = true;
Service_lock->notify_all();
}
void ProtectionDomainCacheTable::unlink() {
assert(SafepointSynchronize::is_at_safepoint(), "must be");
MutexLocker ml(SystemDictionary_lock);
int oops_removed = 0;
for (int i = 0; i < table_size(); ++i) {
ProtectionDomainCacheEntry** p = bucket_addr(i);
ProtectionDomainCacheEntry* entry = bucket(i);
@ -57,7 +65,8 @@ void ProtectionDomainCacheTable::unlink() {
if (pd != NULL) {
p = entry->next_addr();
} else {
LogTarget(Debug, protectiondomain) lt;
oops_removed++;
LogTarget(Debug, protectiondomain, table) lt;
if (lt.is_enabled()) {
LogStream ls(lt);
ls.print_cr("protection domain unlinked at %d", i);
@ -69,9 +78,12 @@ void ProtectionDomainCacheTable::unlink() {
entry = *p;
}
}
_total_oops_removed += oops_removed;
_dead_entries = false;
}
void ProtectionDomainCacheTable::print_on(outputStream* st) const {
assert_locked_or_safepoint(SystemDictionary_lock);
st->print_cr("Protection domain cache table (table_size=%d, classes=%d)",
table_size(), number_of_entries());
for (int index = 0; index < table_size(); index++) {
@ -124,6 +136,7 @@ ProtectionDomainCacheEntry* ProtectionDomainCacheTable::get(Handle protection_do
}
ProtectionDomainCacheEntry* ProtectionDomainCacheTable::find_entry(int index, Handle protection_domain) {
assert_locked_or_safepoint(SystemDictionary_lock);
for (ProtectionDomainCacheEntry* e = bucket(index); e != NULL; e = e->next()) {
if (oopDesc::equals(e->object_no_keepalive(), protection_domain())) {
return e;
@ -138,6 +151,13 @@ ProtectionDomainCacheEntry* ProtectionDomainCacheTable::add_entry(int index, uns
assert(index == index_for(protection_domain), "incorrect index?");
assert(find_entry(index, protection_domain) == NULL, "no double entry");
LogTarget(Debug, protectiondomain, table) lt;
if (lt.is_enabled()) {
LogStream ls(lt);
ls.print("protection domain added ");
protection_domain->print_value_on(&ls);
ls.cr();
}
ClassLoaderWeakHandle w = ClassLoaderWeakHandle::create(protection_domain);
ProtectionDomainCacheEntry* p = new_entry(hash, w);
Hashtable<ClassLoaderWeakHandle, mtClass>::add_entry(index, p);

View File

@ -85,6 +85,9 @@ private:
ProtectionDomainCacheEntry* add_entry(int index, unsigned int hash, Handle protection_domain);
ProtectionDomainCacheEntry* find_entry(int index, Handle protection_domain);
bool _dead_entries;
int _total_oops_removed;
public:
ProtectionDomainCacheTable(int table_size);
ProtectionDomainCacheEntry* get(Handle protection_domain);
@ -93,6 +96,11 @@ public:
void print_on(outputStream* st) const;
void verify();
bool has_work() { return _dead_entries; }
void trigger_cleanup();
int removed_entries_count() { return _total_oops_removed; };
};

View File

@ -1884,7 +1884,7 @@ bool SystemDictionary::do_unloading(GCTimer* gc_timer,
// Oops referenced by the protection domain cache table may get unreachable independently
// of the class loader (eg. cached protection domain oops). So we need to
// explicitly unlink them here.
_pd_cache_table->unlink();
_pd_cache_table->trigger_cleanup();
}
if (do_cleaning) {

View File

@ -376,6 +376,9 @@ public:
// System loader lock
static oop system_loader_lock() { return _system_loader_lock_obj; }
// Protection Domain Table
static ProtectionDomainCacheTable* pd_cache_table() { return _pd_cache_table; }
public:
// Sharing support.
static void reorder_dictionary_for_sharing() NOT_CDS_RETURN;

View File

@ -28,6 +28,7 @@
#include "classfile/classLoaderData.hpp"
#include "classfile/modules.hpp"
#include "classfile/protectionDomainCache.hpp"
#include "classfile/stringTable.hpp"
#include "code/codeCache.hpp"
#include "compiler/methodMatcher.hpp"
@ -1977,6 +1978,10 @@ WB_ENTRY(jint, WB_ResolvedMethodRemovedCount(JNIEnv* env, jobject o))
return (jint) ResolvedMethodTable::removed_entries_count();
WB_END
WB_ENTRY(jint, WB_ProtectionDomainRemovedCount(JNIEnv* env, jobject o))
return (jint) SystemDictionary::pd_cache_table()->removed_entries_count();
WB_END
#define CC (char*)
@ -2199,6 +2204,7 @@ static JNINativeMethod methods[] = {
{CC"printOsInfo", CC"()V", (void*)&WB_PrintOsInfo },
{CC"disableElfSectionCache", CC"()V", (void*)&WB_DisableElfSectionCache },
{CC"resolvedMethodRemovedCount", CC"()I", (void*)&WB_ResolvedMethodRemovedCount },
{CC"protectionDomainRemovedCount", CC"()I", (void*)&WB_ProtectionDomainRemovedCount },
};

View File

@ -23,8 +23,10 @@
*/
#include "precompiled.hpp"
#include "classfile/protectionDomainCache.hpp"
#include "classfile/stringTable.hpp"
#include "classfile/symbolTable.hpp"
#include "classfile/systemDictionary.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/javaCalls.hpp"
#include "runtime/serviceThread.hpp"
@ -88,6 +90,7 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) {
bool stringtable_work = false;
bool symboltable_work = false;
bool resolved_method_table_work = false;
bool protection_domain_table_work = false;
JvmtiDeferredEvent jvmti_event;
{
// Need state transition ThreadBlockInVM so that this thread
@ -107,7 +110,8 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) {
!(has_dcmd_notification_event = DCmdFactory::has_pending_jmx_notification()) &&
!(stringtable_work = StringTable::has_work()) &&
!(symboltable_work = SymbolTable::has_work()) &&
!(resolved_method_table_work = ResolvedMethodTable::has_work())) {
!(resolved_method_table_work = ResolvedMethodTable::has_work()) &&
!(protection_domain_table_work = SystemDictionary::pd_cache_table()->has_work())) {
// wait until one of the sensors has pending requests, or there is a
// pending JVMTI event or JMX GC notification to post
Service_lock->wait(Mutex::_no_safepoint_check_flag);
@ -145,6 +149,10 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) {
if (resolved_method_table_work) {
ResolvedMethodTable::unlink();
}
if (protection_domain_table_work) {
SystemDictionary::pd_cache_table()->unlink();
}
}
}

View File

@ -0,0 +1,88 @@
/*
* Copyright (c) 2018, 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.
*/
/*
* @test
* @summary Verifies the creation and cleaup of entries in the Protection Domain Table
* @library /test/lib
* @modules java.base/jdk.internal.misc
* @build sun.hotspot.WhiteBox
* @run driver ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission
* @run main CleanProtectionDomain
*/
import java.security.ProtectionDomain;
import jdk.test.lib.compiler.InMemoryJavaCompiler;
import jdk.internal.misc.Unsafe;
import static jdk.test.lib.Asserts.*;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
import sun.hotspot.WhiteBox;
public class CleanProtectionDomain {
public static void main(String args[]) throws Exception {
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
"-Xlog:protectiondomain+table=debug",
"--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED",
"-XX:+WhiteBoxAPI",
"-Xbootclasspath/a:.",
Test.class.getName());
OutputAnalyzer output = new OutputAnalyzer(pb.start());
output.shouldContain("protection domain added");
output.shouldContain("protection domain unlinked");
output.shouldHaveExitValue(0);
}
static class Test {
public static void test() throws Exception {
Unsafe unsafe = Unsafe.getUnsafe();
TestClassLoader classloader = new TestClassLoader();
ProtectionDomain pd = new ProtectionDomain(null, null);
byte klassbuf[] = InMemoryJavaCompiler.compile("TestClass", "class TestClass { }");
Class klass = unsafe.defineClass(null, klassbuf, 0, klassbuf.length, classloader, pd);
}
public static void main(String[] args) throws Exception {
WhiteBox wb = WhiteBox.getWhiteBox();
int removedCountOrig = wb.protectionDomainRemovedCount();
int removedCount;
test();
System.gc();
// Wait until ServiceThread cleans ProtectionDomain table.
// When the TestClassLoader is unloaded by GC, at least one
// ProtectionDomainCacheEntry will be eligible for removal.
do {
removedCount = wb.protectionDomainRemovedCount();
} while (removedCountOrig == removedCount);
}
private static class TestClassLoader extends ClassLoader {
public TestClassLoader() {
super();
}
}
}
}

View File

@ -539,4 +539,7 @@ public class WhiteBox {
// Resolved Method Table
public native int resolvedMethodRemovedCount();
// Protection Domain Table
public native int protectionDomainRemovedCount();
}