void (int pagesToCollect) { //Now scan all pages and collect candidates for reclamation. //Basically, build not-fully-up-to-date index for a query like: // `SELECT top <N> FROM pages WHERE usageCount=0 ORDER BY utility DESC` CyclicIterator<PagesTable> pagesTablesToScan = threadSafeCopyOfPagesTables(); int totalPagesCount = Math.max(statistics.totalPagesAllocated() - statistics.totalPagesReclaimed(), 0); pagesForReclaimCollector.startCollectingTurn(pagesToCollect, totalPagesCount); try { int tablesToScan = pagesTablesToScan.size(); for (int pageTableNo = 0; pageTableNo < tablesToScan; pageTableNo++) { PagesTable pageTable = pagesTablesToScan.next(); AtomicReferenceArray<PageImpl> pages = pageTable.pages(); int pagesAliveInTable = 0;//count alive entries to shrink the table if there are too few of them int pagesCount = pages.length(); for (int i = 0; i < pagesCount; i++) { PageImpl page = pages.get(i); if (page == null || page.isTombstone()) { continue; } if (page.isAboutToUnmap() && page.usageCount() == 0) { //reclaim page right now, no need to enqueue it for later: boolean succeed = page.tryMoveTowardsPreTombstone(/*entombYoung: */false); if (succeed) { unmapPageAndReclaimBuffer(page); continue; } } pagesAliveInTable++; //Copy 'usefulness' to field accessed by this thread only -- so comparison by this field is // stable. Shared field .tokensOfUsefulness could be modified concurrently anytime, hence // its use for sorting violates Comparator contract. int tokensOfUsefulness = adjustPageUsefulness(page); page.updateLocalTokensOfUsefulness(tokensOfUsefulness); pagesForReclaimCollector.takePageIfGoodForReclaim(page); } //shrink table if there are too many tombstones: pageTable.shrinkIfNeeded(pagesAliveInTable); //if (pagesForReclaimCollector.hasCollectedEnough()) { // break; //} } } finally { pagesForReclaimCollector.finishCollectingTurn(); } pagesToProbablyReclaim.refill(pagesForReclaimCollector); }