/*
* Copyright (C) 2011-2014 Chris Vest (mr.chrisvest@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package stormpot;
import java.lang.ref.WeakReference;
final class PreciseLeakDetector {
private static final int branchPower =
Integer.getInteger("stormpot.PreciseLeakDetector.branchPower", 5);
private static final int branchFactor = 1 << branchPower;
private static final int branchMask = branchFactor - 1;
private static class WeakRef extends WeakReference<Object> {
WeakRef next;
public WeakRef(Object referent) {
super(referent);
}
}
// Both guarded by synchronized(this)
private Object[] probes;
private int leakedObjectCount;
PreciseLeakDetector() {
// The probes are basically a two-level Bagwell hash trie with linked list
// buckets at the end, to reduce the proliferation of arrays.
// Basically, we compute the identity hash code for an object, and the
// least significant 5 bits are the index into the first-level 32 element
// 'probe' array. When inserting, if that slot is null, then we insert the
// WeakRef directly into that place. In fact, we allow building a WeakRef
// chain up to 4 elements long, directly in the first-level 'probes' array.
// Otherwise we add the second-level 32 element array, rehash the existing
// chain into it, by using the next 5 second-least significant bits as
// index. From here on, we don't create any more levels, but just link the
// WeakRefs together like a daisy-chain. This way, we create at most 33
// arrays, with room for 1024 objects. Most pools probably won't need to
// have more than that many objects registered at a time.
probes = new Object[branchFactor];
}
synchronized void register(Object obj) {
int hash1 = System.identityHashCode(obj) & branchMask;
Object current = probes[hash1];
if (current == null) {
probes[hash1] = new WeakRef(obj);
return;
}
if (current instanceof WeakRef) {
WeakRef currentRef = (WeakRef) current;
if (chainShorterThan(4, current)) {
WeakRef ref = new WeakRef(obj);
ref.next = currentRef;
probes[hash1] = ref;
} else {
WeakRef[] level2 = new WeakRef[branchFactor];
WeakRef ref = new WeakRef(obj);
ref.next = currentRef;
assocLevel2(level2, ref);
probes[hash1] = level2;
}
return;
}
WeakRef[] level2 = (WeakRef[]) current;
assocLevel2(level2, new WeakRef(obj));
}
private void assocLevel2(WeakRef[] level2, WeakRef chain) {
WeakRef next;
Object obj;
while (chain != null) {
next = chain.next;
obj = chain.get();
if (obj != null) {
int hash2 = (System.identityHashCode(obj) >> branchPower) & branchMask;
chain.next = level2[hash2];
level2[hash2] = chain;
} else {
leakedObjectCount++;
}
chain = next;
}
}
private boolean chainShorterThan(int length, Object weakRef) {
WeakRef ref = (WeakRef) weakRef;
while (ref != null) {
if (length == 0) {
return false;
}
length--;
ref = ref.next;
}
return true;
}
synchronized void unregister(Object obj) {
int hash0 = System.identityHashCode(obj);
int hash1 = hash0 & branchMask;
Object current = probes[hash1];
if (current instanceof WeakRef) {
WeakRef ref = (WeakRef) current;
probes[hash1] = removeFromChain(ref, obj);
return;
}
int hash2 = (hash0 >> branchPower) & branchMask;
WeakRef[] level2 = (WeakRef[]) current;
level2[hash2] = removeFromChain(level2[hash2], obj);
}
private WeakRef removeFromChain(WeakRef chain, Object obj) {
WeakRef newChain = null;
while (chain != null) {
WeakRef next = chain.next;
Object current = chain.get();
if (current == null) {
leakedObjectCount++;
} else if (current == obj) {
chain.clear();
} else {
chain.next = newChain;
newChain = chain;
}
chain = next;
}
return newChain;
}
synchronized long countLeakedObjects() {
for (int i = 0; i < probes.length; i++) {
Object current = probes[i];
if (current instanceof WeakRef) {
probes[i] = pruneChain((WeakRef) current);
} else if (current instanceof WeakRef[]) {
WeakRef[] level2 = (WeakRef[]) current;
for (int j = 0; j < level2.length; j++) {
level2[j] = pruneChain(level2[j]);
}
}
}
return leakedObjectCount;
}
private WeakRef pruneChain(WeakRef chain) {
WeakRef newChain = null;
while (chain != null) {
WeakRef next = chain.next;
if (chain.get() == null) {
leakedObjectCount++;
} else {
chain.next = newChain;
newChain = chain;
}
chain = next;
}
return newChain;
}
}