blob: 2ec2dffd982f10bae785925253a39478ecd2e44c [file] [log] [blame]
#include "Incinerator.h"
#include "VmkitGC.h"
#include "Jnjvm.h"
#include "JnjvmClassLoader.h"
#include "JavaThread.h"
#include "JavaClass.h"
#include "VMStaticInstance.h"
#include <algorithm>
#if RESET_STALE_REFERENCES
using namespace std;
namespace j3 {
Incinerator::Incinerator(Jnjvm* j3vm) :
scanRef(Incinerator::scanRef_Disabled),
scanStackRef(Incinerator::scanStackRef_Disabled),
vm(j3vm),
needsStaleRefRescan(false)
#if DEBUG_OBJECT_REF_DUMPING
, findReferencesToObject(NULL)
#endif
{
}
Incinerator::~Incinerator()
{
}
Incinerator* Incinerator::get()
{
vmkit::Thread* th = vmkit::Thread::get();
assert(th && "Invalid current thread.");
if (!th) return NULL;
return &static_cast<Jnjvm*>(th->MyVM)->incinerator;
}
void Incinerator::dumpClassLoaderBundles() const
{
vm->osgi_gateway.dumpClassLoaderBundles();
vmkit::LockGuard lg(lock);
staleBundleClassLoadersType::const_iterator
si = staleBundleClassLoaders.begin(), se = staleBundleClassLoaders.end();
staleBundleClassLoadersType::mapped_type::const_iterator li, le;
for (; si != se; ++si) {
cerr << "stale bundleID=" << si->first << " classLoaders={";
le = si->second.end();
li = si->second.begin();
for (; li != le; ++li) cerr << " " << *li;
cerr << "}" << endl;
}
}
void Incinerator::setBundleStaleReferenceCorrected(OSGiGateway::bundle_id_t bundleID, bool corrected)
{
JnjvmClassLoader * loader = vm->osgi_gateway.getBundleClassLoader(bundleID);
if (!loader) {
vm->illegalArgumentException("Invalid bundle ID"); return;}
#if DEBUG_VERBOSE_STALE_REF
cerr << "Stale references to bundleID=" << bundleID << " are ";
if (corrected)
cerr << "corrected." << endl;
else
cerr << "no more corrected." << endl;
#endif
loader->setStaleReferencesCorrectionEnabled(corrected);
}
bool Incinerator::isBundleStaleReferenceCorrected(OSGiGateway::bundle_id_t bundleID) const
{
JnjvmClassLoader const* loader = vm->osgi_gateway.getBundleClassLoader(bundleID);
if (!loader) {
vm->illegalArgumentException("Invalid bundle ID"); return false;}
return loader->isStaleReferencesCorrectionEnabled();
}
bool Incinerator::UninstalledBundles_finder::operator() (
const staleBundleClassLoadersType::value_type& pair) const
{
staleBundleClassLoadersType::mapped_type::const_iterator
b = pair.second.begin(), e = pair.second.end();
staleBundleClassLoadersType::mapped_type::const_iterator
i = find(b, e, loader);
return (i != e);
}
OSGiGateway::bundle_id_t Incinerator::getClassLoaderBundleID(JnjvmClassLoader const * loader) const
{
if (loader == NULL) return OSGiGateway::invalidBundleID;
OSGiGateway::bundle_id_t r = vm->osgi_gateway.getClassLoaderBundleID(loader);
if (r != OSGiGateway::invalidBundleID) return r;
vmkit::LockGuard lg(lock);
// Look up in stale bundles list
staleBundleClassLoadersType::const_iterator
sb = staleBundleClassLoaders.begin(),
se = staleBundleClassLoaders.end();
staleBundleClassLoadersType::const_iterator
si = find_if(sb, se, UninstalledBundles_finder(loader));
return (si == se) ? OSGiGateway::invalidBundleID : si->first;
}
// Link a bundle ID (OSGi world) to a class loader (Java world).
void Incinerator::setBundleClassLoader(OSGiGateway::bundle_id_t bundleID, JnjvmClassLoader* loader)
{
if (bundleID == OSGiGateway::invalidBundleID) return;
JnjvmClassLoader * previous_loader =
vm->osgi_gateway.getBundleClassLoader(bundleID);
bool bundleUpdated =
(previous_loader != NULL) && (loader != NULL) &&
(previous_loader != loader);
vmkit::LockGuard lg(lock);
if (bundleUpdated) {
// Propagate the stale reference correction setting to the new
// class loader if a previous one exists.
loader->setStaleReferencesCorrectionEnabled(
previous_loader->isStaleReferencesCorrectionEnabled());
}
// Either bundle uninstalled, or bundle updated with a different class loader
if (bundleUpdated || ((previous_loader != NULL) && !loader)) {
// Mark the previous class loader as stale
staleBundleClassLoaders[bundleID].push_front(previous_loader);
previous_loader->markStale(true);
// Enable stale references scanning
setScanningInclusive();
}
vm->osgi_gateway.setBundleClassLoader(bundleID, loader);
}
IncineratorManagedClassLoader::~IncineratorManagedClassLoader()
{
Incinerator& incinerator = JavaThread::get()->getJVM()->incinerator;
incinerator.classLoaderUnloaded(static_cast<JnjvmClassLoader const *>(this));
}
void Incinerator::classLoaderUnloaded(JnjvmClassLoader const * loader)
{
OSGiGateway::bundle_id_t bundleID = getClassLoaderBundleID(loader);
if (bundleID == OSGiGateway::invalidBundleID) {
#if DEBUG_VERBOSE_STALE_REF
cerr << "Class loader unloaded: " << loader << endl;
#endif
return;
}
staleBundleClassLoaders[bundleID].remove(loader);
if (staleBundleClassLoaders[bundleID].size() == 0)
staleBundleClassLoaders.erase(bundleID);
#if DEBUG_VERBOSE_STALE_REF
cerr << "Class loader unloaded: " << loader
<< " bundleID=" << bundleID << endl;
#endif
}
#if DEBUG_OBJECT_REF_DUMPING
void Incinerator::dumpReferencesToObject(const JavaObject* object)
{
pendingRefObject.clear();
pendingRefObject.push_back(object);
seenObjects.clear();
const JavaObject* obj = NULL;
do {
obj = pendingRefObject.back();
pendingRefObject.pop_back();
seenObjects.insert(obj);
cerr << "dumpReferencesToObject: " << *obj << ", toString=";
obj->dumpToString();
findReferencesToObject = obj;
foundReferencerObjects.clear();
setScanningInclusive();
vmkit::Collector::collect();
} while (!pendingRefObject.empty());
}
#endif
void Incinerator::forceStaleReferenceScanning()
{
setScanningInclusive();
vmkit::Collector::collect();
}
bool Incinerator::isScanningEnabled()
{
return scanRef != Incinerator::scanRef_Disabled;
}
void Incinerator::setScanningDisabled()
{
scanRef = Incinerator::scanRef_Disabled;
scanStackRef = Incinerator::scanStackRef_Disabled;
#if DEBUG_VERBOSE_STALE_REF
cerr << "References scan done." << endl;
#endif
}
void Incinerator::setScanningInclusive()
{
scanRef = Incinerator::scanRef_Inclusive;
scanStackRef = Incinerator::scanStackRef_Inclusive;
#if DEBUG_VERBOSE_STALE_REF
cerr << "References scan enabled." << endl;
#endif
}
void Incinerator::setScanningExclusive()
{
#if DEBUG_EXCLUDE_FINALIZABLE_STALE_OBJECTS
scanRef = Incinerator::scanRef_Exclusive;
scanStackRef = Incinerator::scanStackRef_Exclusive;
#if DEBUG_VERBOSE_STALE_REF
cerr << "Excluding stale references..." << endl;
#endif
#endif
}
void Incinerator::beforeCollection()
{
#if DEBUG_OBJECT_REF_DUMPING
if (findReferencesToObject != NULL)
foundReferencerObjects.clear();
#endif
#if DEBUG_VERBOSE_STALE_REF
if (needsStaleRefRescan) {
cerr << "Some stale references were previously ignored due to"
" finalizable stale objects." << endl;
}
#endif
if (!needsStaleRefRescan && !isScanningEnabled()) {
#if DEBUG_OBJECT_REF_DUMPING
if (!findReferencesToObject)
#endif
return;
}
needsStaleRefRescan = false;
setScanningInclusive();
}
void Incinerator::markingFinalizersDone()
{
#if DEBUG_OBJECT_REF_DUMPING
if (findReferencesToObject != NULL) return;
#endif
if (!isScanningEnabled()) return;
setScanningExclusive();
}
void Incinerator::collectorPhaseComplete()
{
for (StaleRefListType::const_iterator
i = staleRefList.begin(), e = staleRefList.end(); i != e; ++i)
{
eliminateStaleRef(i->second, i->first);
}
staleRefList.clear();
}
void Incinerator::afterCollection()
{
#if DEBUG_OBJECT_REF_DUMPING
findReferencesToObject = NULL;
#endif
if (!isScanningEnabled()) return;
#if DEBUG_VERBOSE_STALE_REF
if (needsStaleRefRescan) {
cerr << "Some stale references were ignored due to finalizable"
" stale objects. Another garbage collection is needed." << endl;
}
#endif
setScanningDisabled();
}
bool Incinerator::isStaleObject(const JavaObject* obj)
{
llvm_gcroot(obj, 0);
if (!obj) return false;
const JnjvmClassLoader* loader = NULL;
if (VMClassLoader::isVMClassLoader(obj)) {
loader = ((const VMClassLoader*)obj)->getClassLoader();
} else if (VMStaticInstance::isVMStaticInstance(obj)) {
loader = ((const VMStaticInstance*)obj)->getOwningClass()->classLoader;
} else {
CommonClass* ccl = JavaObject::getClass(obj);
assert (ccl && "Object Class is not null.");
loader = ccl->classLoader;
}
return loader->isStale() && loader->isStaleReferencesCorrectionEnabled();
}
bool Incinerator::isVMObject(const JavaObject* obj)
{
llvm_gcroot(obj, 0);
// Check the type of Java object.
// Some Java objects are not real object, but are bridges between Java
// and the VM C++ objects.
return (obj != NULL) && (
VMClassLoader::isVMClassLoader(obj)
|| VMStaticInstance::isVMStaticInstance(obj));
}
bool Incinerator::scanRef_Disabled(Incinerator&, const JavaObject* source, JavaObject** ref)
{
llvm_gcroot(source, 0);
#if DEBUG_VERBOSE_STALE_REF
if (!ref || !(*ref)) return true;
if (JavaObject::getClass(*ref)->name->elements[0] == 'i')
cout << ref << " ==> " << **ref << endl;
#endif
return true;
}
bool Incinerator::scanStackRef_Disabled(Incinerator&, const JavaMethod* method, JavaObject** ref)
{
#if DEBUG_VERBOSE_STALE_REF
if (!ref || !(*ref)) return true;
if (method && method->classDef->name->elements[0] == 'i')
cout << *method << ": " << ref << " ==> " << **ref << endl;
#endif
return true;
}
bool Incinerator::scanRef_Inclusive(Incinerator& incinerator, const JavaObject* source, JavaObject** ref)
{
llvm_gcroot(source, 0);
if (!ref) return true;
#if DEBUG_OBJECT_REF_DUMPING
if (incinerator.findReferencesToObject != NULL) {
if (incinerator.findReferencesToObject == *ref) {
if (!source) {
cout << *ref << " <-- (unknown)" << endl;
} else {
if (incinerator.foundReferencerObjects.insert(source).second) {
cout << *ref << " <-- " << *source << endl;
if (incinerator.seenObjects.find(source) == incinerator.seenObjects.end()) {
// The source ref was not scanned before
std::deque<const JavaObject*>::iterator
b = incinerator.pendingRefObject.begin(),
e = incinerator.pendingRefObject.end();
if (std::find(b, e, source) == e) {
// The source ref is not currently pending for scan
incinerator.pendingRefObject.push_front(source);
}
}
}
}
}
return true;
}
#endif
if (!isStaleObject(*ref)) return true;
#if DEBUG_VERBOSE_STALE_REF
if (isVMObject(*ref))
cerr << "Stale ref (VMObject): ";
else
cerr << "Stale ref: ";
cerr << ref << " ==> " << **ref;
if (source) cerr << " source=" << *source;
cerr << endl;
#endif
if (isVMObject(*ref))
return true;
// Queue the stale reference to be eliminated.
incinerator.staleRefList[ref] = source;
// Skip this reference and don't trace it.
return false;
}
bool Incinerator::scanStackRef_Inclusive(Incinerator& incinerator, const JavaMethod* method, JavaObject** ref)
{
#if DEBUG_OBJECT_REF_DUMPING
if (ref != NULL && incinerator.findReferencesToObject != NULL) {
if (incinerator.findReferencesToObject == *ref) {
cout << "On stack of method=" << *method->name << " ,class=" << *method->classDef << endl;
}
}
#endif
#if DEBUG_VERBOSE_STALE_REF
if (!ref || !(*ref)) return true;
if (method && method->classDef->name->elements[0] == 'i')
cout << *method << ": " << ref << " ==> " << **ref << endl;
#endif
return Incinerator::scanRef_Inclusive(incinerator, NULL, ref);
}
bool Incinerator::scanRef_Exclusive(Incinerator& incinerator, const JavaObject* source, JavaObject** ref)
{
llvm_gcroot(source, 0);
// Do not eliminate any stale references traced via finalizable objects.
if ((ref != NULL) && isStaleObject(*ref)) {
#if DEBUG_VERBOSE_STALE_REF
size_t removed =
#endif
incinerator.staleRefList.erase(ref);
incinerator.needsStaleRefRescan = true;
#if DEBUG_VERBOSE_STALE_REF
if (!removed)
cerr << "Stale ref (ignored): " << ref << " ==> " << **ref;
else
cerr << "Excluded stale ref: " << ref << " ==> " << **ref;
if (source) cerr << " source=" << *source;
cerr << endl;
#endif
}
return true; // Trace this reference.
}
bool Incinerator::scanStackRef_Exclusive(Incinerator& incinerator, const JavaMethod* method, JavaObject** ref)
{
#if DEBUG_VERBOSE_STALE_REF
if (!ref || !(*ref)) return true;
if (method && method->classDef->name->elements[0] == 'i')
cout << *method << ": " << ref << " ==> " << **ref << endl;
#endif
return Incinerator::scanRef_Exclusive(incinerator, NULL, ref);
}
void Incinerator::eliminateStaleRef(const JavaObject *source, JavaObject** ref)
{
if (isVMObject(*ref))
return;
CommonClass* ccl = JavaObject::getClass(*ref);
assert (ccl && "Object Class is not null.");
#if DEBUG_VERBOSE_STALE_REF
cerr << "Resetting stale ref=" << ref << " obj=" << **ref << " classLoader=" << ccl->classLoader;
if (source) cerr << " source=" << *source;
cerr << endl;
#endif
if (!ccl->classLoader->isStaleReferencesCorrectionEnabled()) {
#if DEBUG_VERBOSE_STALE_REF
cerr << "WARNING: Ignoring stale ref=" << ref << " obj=" << **ref << " classLoader=" << ccl->classLoader;
if (source) cerr << " source=" << *source;
cerr << endl;
#endif
return;
}
if (JavaThread* ownerThread = (JavaThread*)vmkit::ThinLock::getOwner(*ref, vm->lockSystem)) {
if (vmkit::FatLock* lock = vmkit::ThinLock::getFatLock(*ref, vm->lockSystem))
lock->markAssociatedObjectAsDead();
// Notify all threads waiting on this object
ownerThread->lockingThread.notifyAll(*ref, vm->lockSystem, ownerThread);
// Release this object
while (vmkit::ThinLock::getOwner(*ref, vm->lockSystem) == ownerThread)
vmkit::ThinLock::release(*ref, vm->lockSystem, ownerThread);
}
*ref = NULL; // Reset the reference
}
}
extern "C" void Java_j3_vm_OSGi_setBundleStaleReferenceCorrected(jlong bundleID, jboolean corrected)
{
#if RESET_STALE_REFERENCES
j3::Incinerator::get()->setBundleStaleReferenceCorrected(bundleID, corrected);
#endif
}
extern "C" jboolean Java_j3_vm_OSGi_isBundleStaleReferenceCorrected(jlong bundleID)
{
#if RESET_STALE_REFERENCES
return j3::Incinerator::get()->isBundleStaleReferenceCorrected(bundleID);
#else
return false;
#endif
}
#if DEBUG_OBJECT_REF_DUMPING
extern "C" void Java_j3_vm_OSGi_dumpReferencesToObject(jlong obj)
{
#if RESET_STALE_REFERENCES
j3::Incinerator::get()->dumpReferencesToObject(reinterpret_cast<const j3::JavaObject*>(obj));
#endif
}
#endif
extern "C" void Java_j3_vm_OSGi_forceStaleReferenceScanning()
{
#if RESET_STALE_REFERENCES
j3::Incinerator::get()->forceStaleReferenceScanning();
#endif
}
#endif