/**
* Cleans up after garbage-collected thread locals.
*/
private void cleanUp() {
if (rehash()) {
// If we rehashed, we needn't clean up (clean up happens as
// a side effect).
return;
}
if (size == 0) {
// No live entries == nothing to clean.
return;
}
// Clean log(table.length) entries picking up where we left off
// last time.
int index = clean;
Object[] table = this.table;
for (int counter = table.length; counter > 0; counter >>= 1,
index = next(index)) {
Object k = table[index];
if (k == TOMBSTONE || k == null) {
continue; // on to next entry
}
// The table can only contain null, tombstones and references.
@SuppressWarnings("unchecked")
Reference
> reference
= (Reference
>) k; if (reference.get() == null) { // This thread local was reclaimed by the garbage collector. table[index] = TOMBSTONE; table[index + 1] = null; tombstones++; size--; } } // Point cursor to next index. clean = index; }
从代码可以知道,ThreadLocal 对象被回收了,但是它的弱引用所对应的value却还存在,通过value=null来释放value所占用的内存。并给它设置一个标志用TOMBSTONE。表示这块内存已经被释放了。
这样上面 firstTombstone 这个字段的含义也一目了然了。
到这里我们思考一下,既然使用数组来保存对象,数组肯定要扩容啊,没错,确实有这个方法,叫rehash()
/**
* Rehashes the table, expanding or contracting it as necessary.
* Gets rid of tombstones. Returns true if a rehash occurred.
* We must rehash every time we fill a null slot; we depend on the
* presence of null slots to end searches (otherwise, we'll infinitely
* loop).
*/
private boolean rehash() {
if (tombstones + size < maximumLoad) {
return false;
}
int capacity = table.length >> 1;
// Default to the same capacity. This will create a table of the
// same size and move over the live entries, analogous to a
// garbage collection. This should only happen if you churn a
// bunch of thread local garbage (removing and reinserting
// the same thread locals over and over will overwrite tombstones
// and not fill up the table).
int newCapacity = capacity;
if (size > (capacity >> 1)) {
// More than 1/2 filled w/ live entries.
// Double size.
newCapacity = capacity * 2;
}
Object[] oldTable = this.table;
// Allocate new table.
initializeTable(newCapacity);
// We won't have any tombstones after this.
this.tombstones = 0;
// If we have no live entries, we can quit here.
if (size == 0) {
return true;
}
// Move over entries.
for (int i = oldTable.length - 2; i >= 0; i -= 2) {
Object k = oldTable[i];
if (k == null || k == TOMBSTONE) {
// Skip this entry.
continue;
}
// The table can only contain null, tombstones and references.
@SuppressWarnings("unchecked")
Reference
> reference
= (Reference
>) k; ThreadLocal
key = reference.get(); if (key != null) { // Entry is still live. Move it over. add(key, oldTable[i + 1]); } else { // The key was reclaimed. size--; } } return true; }
就是当前正在使用的内存大于分配内存的1/2的时候进行数扩容。还有最后一个方法就是remove了,用来释放内存。
/** * Removes entry for the given ThreadLocal. */ void remove(ThreadLocal key) { cleanUp(); for (int index = key.hash & mask;; index = next(index)) { Object reference = table[index]; if (reference == key.reference) { // Success! table[index] = TOMBSTONE; table[index + 1] = null; tombstones++; size--; return; } if (reference == null) { // No entry found. return; } } }通过代码分析也可以知道,也用TOMBSTONE来表示这块内存已经被释放掉了,有兴趣的朋友可以仔细研究代码。最后分析下本篇文章最长用到的一个函数:/** * Gets the next index. If we're at the end of the table, we wrap back * around to 0. */ private int next(int index) { return (index + 2) & mask; }这方法的好处是可以循环遍历,嗯 ,以后借鉴这种方法也是不错的。
本人最后提醒一下:1 使用ThreadLocal 要真正实行线程之间数据无干扰的话,必须要进行对象深拷贝。 2 使用Thr