/** * Callback routine called by JVM after full gc run. Has two functions: 1) sets the amount of * memory to be cleaned from the cache by the Cleaner 2) sets the CAN_ALLOC flag to false if * memory level is critical * * <p>The callback happens in a system thread, and hence not through the usual water.Boot loader * - and so any touched classes are in the wrong class loader and you end up with new classes * with uninitialized global vars. Limit to touching global vars in the Boot class. */ public void handleNotification(Notification notification, Object handback) { String notifType = notification.getType(); if (notifType.equals(MemoryNotificationInfo.MEMORY_COLLECTION_THRESHOLD_EXCEEDED)) { // Memory used after this FullGC Boot.TIME_AT_LAST_GC = System.currentTimeMillis(); Boot.HEAP_USED_AT_LAST_GC = _allMemBean.getHeapMemoryUsage().getUsed(); Boot.kick_store_cleaner(); } }
// Set K/V cache goals. // Allow (or disallow) allocations. // Called from the Cleaner, when "cacheUsed" has changed significantly. // Called from any FullGC notification, and HEAP/POJO_USED changed. // Called on any OOM allocation public static void set_goals(String msg, boolean oom, long bytes) { // Our best guess of free memory, as of the last GC cycle final long heapUsed = Boot.HEAP_USED_AT_LAST_GC; final long timeGC = Boot.TIME_AT_LAST_GC; final long freeHeap = MEM_MAX - heapUsed; assert freeHeap >= 0 : "I am really confused about the heap usage; MEM_MAX=" + MEM_MAX + " heapUsed=" + heapUsed; // Current memory held in the K/V store. final long cacheUsage = myHisto.histo(false)._cached; // Our best guess of POJO object usage: Heap_used minus cache used final long pojoUsedGC = Math.max(heapUsed - cacheUsage, 0); // Block allocations if: // the cache is > 7/8 MEM_MAX, OR // we cannot allocate an equal amount of POJOs, pojoUsedGC > freeHeap. // Decay POJOS_USED by 1/8th every 5 sec: assume we got hit with a single // large allocation which is not repeating - so we do not need to have // double the POJO amount. // Keep at least 1/8th heap for caching. // Emergency-clean the cache down to the blocking level. long d = MEM_CRITICAL; // Decay POJO amount long p = pojoUsedGC; long age = (System.currentTimeMillis() - timeGC); // Age since last FullGC age = Math.min(age, 10 * 60 * 1000); // Clip at 10mins while ((age -= 5000) > 0) p = p - (p >> 3); // Decay effective POJO by 1/8th every 5sec d -= 2 * p - bytes; // Allow for the effective POJO, and again to throttle GC rate d = Math.max(d, MEM_MAX >> 3); // Keep at least 1/8th heap H2O.Cleaner.DESIRED = d; String m = ""; if (cacheUsage > H2O.Cleaner.DESIRED) { m = (CAN_ALLOC ? "Blocking! " : "blocked: "); if (oom) setMemLow(); // Stop allocations; trigger emergency clean Boot.kick_store_cleaner(); } else { // Else we are not *emergency* cleaning, but may be lazily cleaning. if (!CAN_ALLOC) m = "Unblocking:"; else m = "MemGood: "; setMemGood(); if (oom) // Confused? OOM should have FullGCd should have set low-mem goals Log.warn( Sys.CLEAN, "OOM but no FullGC callback? MEM_MAX = " + MEM_MAX + ", DESIRED = " + d + ", CACHE = " + cacheUsage + ", p = " + p + ", bytes = " + bytes); } // No logging if under memory pressure: can deadlock the cleaner thread if (Log.flag(Sys.CLEAN)) { String s = m + msg + ", HEAP_LAST_GC=" + (heapUsed >> 20) + "M, KV=" + (cacheUsage >> 20) + "M, POJO=" + (pojoUsedGC >> 20) + "M, free=" + (freeHeap >> 20) + "M, MAX=" + (MEM_MAX >> 20) + "M, DESIRED=" + (H2O.Cleaner.DESIRED >> 20) + "M" + (oom ? " OOM!" : " NO-OOM"); if (CAN_ALLOC) Log.debug(Sys.CLEAN, s); else Log.unwrap(System.err, s); } }