8206424: Use locking for cleaning ProtectionDomainTable
ServiceThread is now in charge of cleaning ProtectionDomainTable entries Reviewed-by: coleenp, iklam
This commit is contained in:
parent
98242123a3
commit
16b92a561c
@ -44,11 +44,19 @@ int ProtectionDomainCacheTable::index_for(Handle protection_domain) {
|
|||||||
|
|
||||||
ProtectionDomainCacheTable::ProtectionDomainCacheTable(int table_size)
|
ProtectionDomainCacheTable::ProtectionDomainCacheTable(int table_size)
|
||||||
: Hashtable<ClassLoaderWeakHandle, mtClass>(table_size, sizeof(ProtectionDomainCacheEntry))
|
: 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() {
|
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) {
|
for (int i = 0; i < table_size(); ++i) {
|
||||||
ProtectionDomainCacheEntry** p = bucket_addr(i);
|
ProtectionDomainCacheEntry** p = bucket_addr(i);
|
||||||
ProtectionDomainCacheEntry* entry = bucket(i);
|
ProtectionDomainCacheEntry* entry = bucket(i);
|
||||||
@ -57,7 +65,8 @@ void ProtectionDomainCacheTable::unlink() {
|
|||||||
if (pd != NULL) {
|
if (pd != NULL) {
|
||||||
p = entry->next_addr();
|
p = entry->next_addr();
|
||||||
} else {
|
} else {
|
||||||
LogTarget(Debug, protectiondomain) lt;
|
oops_removed++;
|
||||||
|
LogTarget(Debug, protectiondomain, table) lt;
|
||||||
if (lt.is_enabled()) {
|
if (lt.is_enabled()) {
|
||||||
LogStream ls(lt);
|
LogStream ls(lt);
|
||||||
ls.print_cr("protection domain unlinked at %d", i);
|
ls.print_cr("protection domain unlinked at %d", i);
|
||||||
@ -69,9 +78,12 @@ void ProtectionDomainCacheTable::unlink() {
|
|||||||
entry = *p;
|
entry = *p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_total_oops_removed += oops_removed;
|
||||||
|
_dead_entries = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProtectionDomainCacheTable::print_on(outputStream* st) const {
|
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)",
|
st->print_cr("Protection domain cache table (table_size=%d, classes=%d)",
|
||||||
table_size(), number_of_entries());
|
table_size(), number_of_entries());
|
||||||
for (int index = 0; index < table_size(); index++) {
|
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) {
|
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()) {
|
for (ProtectionDomainCacheEntry* e = bucket(index); e != NULL; e = e->next()) {
|
||||||
if (oopDesc::equals(e->object_no_keepalive(), protection_domain())) {
|
if (oopDesc::equals(e->object_no_keepalive(), protection_domain())) {
|
||||||
return e;
|
return e;
|
||||||
@ -138,6 +151,13 @@ ProtectionDomainCacheEntry* ProtectionDomainCacheTable::add_entry(int index, uns
|
|||||||
assert(index == index_for(protection_domain), "incorrect index?");
|
assert(index == index_for(protection_domain), "incorrect index?");
|
||||||
assert(find_entry(index, protection_domain) == NULL, "no double entry");
|
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);
|
ClassLoaderWeakHandle w = ClassLoaderWeakHandle::create(protection_domain);
|
||||||
ProtectionDomainCacheEntry* p = new_entry(hash, w);
|
ProtectionDomainCacheEntry* p = new_entry(hash, w);
|
||||||
Hashtable<ClassLoaderWeakHandle, mtClass>::add_entry(index, p);
|
Hashtable<ClassLoaderWeakHandle, mtClass>::add_entry(index, p);
|
||||||
|
@ -85,6 +85,9 @@ private:
|
|||||||
ProtectionDomainCacheEntry* add_entry(int index, unsigned int hash, Handle protection_domain);
|
ProtectionDomainCacheEntry* add_entry(int index, unsigned int hash, Handle protection_domain);
|
||||||
ProtectionDomainCacheEntry* find_entry(int index, Handle protection_domain);
|
ProtectionDomainCacheEntry* find_entry(int index, Handle protection_domain);
|
||||||
|
|
||||||
|
bool _dead_entries;
|
||||||
|
int _total_oops_removed;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ProtectionDomainCacheTable(int table_size);
|
ProtectionDomainCacheTable(int table_size);
|
||||||
ProtectionDomainCacheEntry* get(Handle protection_domain);
|
ProtectionDomainCacheEntry* get(Handle protection_domain);
|
||||||
@ -93,6 +96,11 @@ public:
|
|||||||
|
|
||||||
void print_on(outputStream* st) const;
|
void print_on(outputStream* st) const;
|
||||||
void verify();
|
void verify();
|
||||||
|
|
||||||
|
bool has_work() { return _dead_entries; }
|
||||||
|
void trigger_cleanup();
|
||||||
|
|
||||||
|
int removed_entries_count() { return _total_oops_removed; };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -1884,7 +1884,7 @@ bool SystemDictionary::do_unloading(GCTimer* gc_timer,
|
|||||||
// Oops referenced by the protection domain cache table may get unreachable independently
|
// 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
|
// of the class loader (eg. cached protection domain oops). So we need to
|
||||||
// explicitly unlink them here.
|
// explicitly unlink them here.
|
||||||
_pd_cache_table->unlink();
|
_pd_cache_table->trigger_cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (do_cleaning) {
|
if (do_cleaning) {
|
||||||
|
@ -376,6 +376,9 @@ public:
|
|||||||
// System loader lock
|
// System loader lock
|
||||||
static oop system_loader_lock() { return _system_loader_lock_obj; }
|
static oop system_loader_lock() { return _system_loader_lock_obj; }
|
||||||
|
|
||||||
|
// Protection Domain Table
|
||||||
|
static ProtectionDomainCacheTable* pd_cache_table() { return _pd_cache_table; }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Sharing support.
|
// Sharing support.
|
||||||
static void reorder_dictionary_for_sharing() NOT_CDS_RETURN;
|
static void reorder_dictionary_for_sharing() NOT_CDS_RETURN;
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
#include "classfile/classLoaderData.hpp"
|
#include "classfile/classLoaderData.hpp"
|
||||||
#include "classfile/modules.hpp"
|
#include "classfile/modules.hpp"
|
||||||
|
#include "classfile/protectionDomainCache.hpp"
|
||||||
#include "classfile/stringTable.hpp"
|
#include "classfile/stringTable.hpp"
|
||||||
#include "code/codeCache.hpp"
|
#include "code/codeCache.hpp"
|
||||||
#include "compiler/methodMatcher.hpp"
|
#include "compiler/methodMatcher.hpp"
|
||||||
@ -1977,6 +1978,10 @@ WB_ENTRY(jint, WB_ResolvedMethodRemovedCount(JNIEnv* env, jobject o))
|
|||||||
return (jint) ResolvedMethodTable::removed_entries_count();
|
return (jint) ResolvedMethodTable::removed_entries_count();
|
||||||
WB_END
|
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*)
|
#define CC (char*)
|
||||||
|
|
||||||
@ -2199,6 +2204,7 @@ static JNINativeMethod methods[] = {
|
|||||||
{CC"printOsInfo", CC"()V", (void*)&WB_PrintOsInfo },
|
{CC"printOsInfo", CC"()V", (void*)&WB_PrintOsInfo },
|
||||||
{CC"disableElfSectionCache", CC"()V", (void*)&WB_DisableElfSectionCache },
|
{CC"disableElfSectionCache", CC"()V", (void*)&WB_DisableElfSectionCache },
|
||||||
{CC"resolvedMethodRemovedCount", CC"()I", (void*)&WB_ResolvedMethodRemovedCount },
|
{CC"resolvedMethodRemovedCount", CC"()I", (void*)&WB_ResolvedMethodRemovedCount },
|
||||||
|
{CC"protectionDomainRemovedCount", CC"()I", (void*)&WB_ProtectionDomainRemovedCount },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,8 +23,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "precompiled.hpp"
|
#include "precompiled.hpp"
|
||||||
|
#include "classfile/protectionDomainCache.hpp"
|
||||||
#include "classfile/stringTable.hpp"
|
#include "classfile/stringTable.hpp"
|
||||||
#include "classfile/symbolTable.hpp"
|
#include "classfile/symbolTable.hpp"
|
||||||
|
#include "classfile/systemDictionary.hpp"
|
||||||
#include "runtime/interfaceSupport.inline.hpp"
|
#include "runtime/interfaceSupport.inline.hpp"
|
||||||
#include "runtime/javaCalls.hpp"
|
#include "runtime/javaCalls.hpp"
|
||||||
#include "runtime/serviceThread.hpp"
|
#include "runtime/serviceThread.hpp"
|
||||||
@ -88,6 +90,7 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) {
|
|||||||
bool stringtable_work = false;
|
bool stringtable_work = false;
|
||||||
bool symboltable_work = false;
|
bool symboltable_work = false;
|
||||||
bool resolved_method_table_work = false;
|
bool resolved_method_table_work = false;
|
||||||
|
bool protection_domain_table_work = false;
|
||||||
JvmtiDeferredEvent jvmti_event;
|
JvmtiDeferredEvent jvmti_event;
|
||||||
{
|
{
|
||||||
// Need state transition ThreadBlockInVM so that this thread
|
// 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()) &&
|
!(has_dcmd_notification_event = DCmdFactory::has_pending_jmx_notification()) &&
|
||||||
!(stringtable_work = StringTable::has_work()) &&
|
!(stringtable_work = StringTable::has_work()) &&
|
||||||
!(symboltable_work = SymbolTable::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
|
// wait until one of the sensors has pending requests, or there is a
|
||||||
// pending JVMTI event or JMX GC notification to post
|
// pending JVMTI event or JMX GC notification to post
|
||||||
Service_lock->wait(Mutex::_no_safepoint_check_flag);
|
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) {
|
if (resolved_method_table_work) {
|
||||||
ResolvedMethodTable::unlink();
|
ResolvedMethodTable::unlink();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (protection_domain_table_work) {
|
||||||
|
SystemDictionary::pd_cache_table()->unlink();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -539,4 +539,7 @@ public class WhiteBox {
|
|||||||
|
|
||||||
// Resolved Method Table
|
// Resolved Method Table
|
||||||
public native int resolvedMethodRemovedCount();
|
public native int resolvedMethodRemovedCount();
|
||||||
|
|
||||||
|
// Protection Domain Table
|
||||||
|
public native int protectionDomainRemovedCount();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user