/** * Unregister a resource from this tracker. * * @param resource to be unregistered from this tracker. */ public void unregisterResource(final Object resource) { LOG.debug("Unregistering resource {}.", resource); synchronized (mReferences) { final List<ResourceReference> refs = mReferences.get(System.identityHashCode(resource)); for (int i = 0; i < refs.size(); i++) { final ResourceReference ref = refs.get(i); // The referent is guaranteed to be present, because the argument is a strong reference if (resource == ref.get()) { refs.remove(i); return; } } } CLEANUP_LOG.warn("Attempted to unregister an untracked resource: {}. At\n{}", resource, Joiner.on('\n').join(Iterables.skip(Arrays.asList(new Exception().getStackTrace()), 2))); }
/** {@inheritDoc} */ @Override public void run() { try { while (true) { ResourceReference ref = (ResourceReference) mReferenceQueue.remove(); synchronized (mReferences) { // Remove multiple times in case of multiple registrations while (mReferences.remove(ref.getIdentityHash(), ref)) { logReference(ref); } } ref.clear(); // Clear the reference to indicate we are finished with it } } catch (InterruptedException e) { // If this thread is interrupted, then die. This happens normally when // ReferenceTracker#close is called. // Restore the interrupted status Thread.currentThread().interrupt(); } } }
/** * Register a resource to be tracked. * * @param resource to be registered to this tracker. * @param message associated with the resource. * @param stackTrace associated with the resource. */ public void registerResource( final Object resource, final String message, final String stackTrace ) { LOG.debug("Registering resource {}.", resource); final ResourceReference ref = new ResourceReference(mReferenceQueue, resource, message, stackTrace); synchronized (mReferences) { mReferences.put(ref.getIdentityHash(), ref); } }
/** * Returns whether the resource is registered with this resource tracker. This is mostly * useful for testing the reference tracker itself. * * @param resource The resource to test for registration. * @return Whether the provided resource is registered. */ public boolean resourceIsRegistered(final Object resource) { synchronized (mReferences) { final List<ResourceReference> refs = mReferences.get(System.identityHashCode(resource)); for (final ResourceReference ref : refs) { // The referent is guaranteed to be present, because the argument is a strong reference if (resource == ref.get()) { return true; } } } return false; }
/** * Close this {@code ReferenceTracker}. Will log any outstanding registered resources. */ @Override public void close() { mExecutorService.shutdownNow(); synchronized (mReferences) { for (ResourceReference reference : mReferences.values()) { logReference(reference); reference.clear(); // Prevent the reference from being enqueued } mReferences.clear(); } }
/** * Log a reference in the cleanup log with it's message. * * @param reference to log. */ private static void logReference(ResourceReference reference) { CLEANUP_LOG.error("Leaked resource detected: {}\n{}", reference.getMessage(), reference.getStackTrace()); }