void MemoryPressureHandler::install() { if (m_installed || m_holdOffTimer.isActive()) return; if (!tryEnsureEventFD()) return; m_eventFDPoller = std::make_unique<EventFDPoller>(m_eventFD.value(), [this] { // FIXME: Current memcg does not provide any way for users to know how serious the memory pressure is. // So we assume all notifications from memcg are critical for now. If memcg had better inferfaces // to get a detailed memory pressure level in the future, we should update here accordingly. bool critical = true; if (ReliefLogger::loggingEnabled()) LOG(MemoryPressure, "Got memory pressure notification (%s)", critical ? "critical" : "non-critical"); setUnderMemoryPressure(critical); if (isMainThread()) respondToMemoryPressure(critical ? Critical::Yes : Critical::No); else RunLoop::main().dispatch([this, critical] { respondToMemoryPressure(critical ? Critical::Yes : Critical::No); }); }); if (ReliefLogger::loggingEnabled() && isUnderMemoryPressure()) LOG(MemoryPressure, "System is no longer under memory pressure."); setUnderMemoryPressure(false); m_installed = true; }
void MemoryPressureHandler::releaseCriticalMemory(Synchronous synchronous) { { ReliefLogger log("Empty the PageCache"); // Right now, the only reason we call release critical memory while not under memory pressure is if the process is about to be suspended. PruningReason pruningReason = isUnderMemoryPressure() ? PruningReason::MemoryPressure : PruningReason::ProcessSuspended; PageCache::singleton().pruneToSizeNow(0, pruningReason); } { ReliefLogger log("Prune MemoryCache live resources"); MemoryCache::singleton().pruneLiveResourcesToSize(0); } { ReliefLogger log("Drain CSSValuePool"); CSSValuePool::singleton().drain(); } { ReliefLogger log("Discard StyleResolvers"); Vector<RefPtr<Document>> documents; copyToVector(Document::allDocuments(), documents); for (auto& document : documents) document->clearStyleResolver(); } { ReliefLogger log("Discard all JIT-compiled code"); GCController::singleton().deleteAllCode(); } { ReliefLogger log("Invalidate font cache"); FontCache::singleton().invalidate(); } #if ENABLE(VIDEO) { ReliefLogger log("Dropping buffered data from paused media elements"); for (auto* mediaElement: HTMLMediaElement::allMediaElements()) { if (mediaElement->paused()) mediaElement->purgeBufferedDataIfPossible(); } } #endif if (synchronous == Synchronous::Yes) { ReliefLogger log("Collecting JavaScript garbage"); GCController::singleton().garbageCollectNow(); } else GCController::singleton().garbageCollectNowIfNotDoneRecently(); }
void MemoryPressureHandler::releaseCriticalMemory(Synchronous synchronous) { { ReliefLogger log("Empty the PageCache"); // Right now, the only reason we call release critical memory while not under memory pressure is if the process is about to be suspended. PruningReason pruningReason = isUnderMemoryPressure() ? PruningReason::MemoryPressure : PruningReason::ProcessSuspended; PageCache::singleton().pruneToSizeNow(0, pruningReason); } { ReliefLogger log("Prune MemoryCache live resources"); MemoryCache::singleton().pruneLiveResourcesToSize(0, /*shouldDestroyDecodedDataForAllLiveResources*/ true); } { ReliefLogger log("Drain CSSValuePool"); CSSValuePool::singleton().drain(); } { ReliefLogger log("Discard StyleResolvers"); Vector<RefPtr<Document>> documents; copyToVector(Document::allDocuments(), documents); for (auto& document : documents) document->clearStyleResolver(); } { ReliefLogger log("Discard all JIT-compiled code"); GCController::singleton().deleteAllCode(); } #if ENABLE(VIDEO) { ReliefLogger log("Dropping buffered data from paused media elements"); for (auto* mediaElement: HTMLMediaElement::allMediaElements()) { if (mediaElement->paused()) mediaElement->purgeBufferedDataIfPossible(); } } #endif if (synchronous == Synchronous::Yes) { ReliefLogger log("Collecting JavaScript garbage"); GCController::singleton().garbageCollectNow(); } else GCController::singleton().garbageCollectNowIfNotDoneRecently(); // We reduce tiling coverage while under memory pressure, so make sure to drop excess tiles ASAP. Page::forEachPage([](Page& page) { page.chrome().client().scheduleCompositingLayerFlush(); }); }
void MemoryPressureHandler::install() { if (m_installed) return; m_eventFD = eventfd(0, EFD_CLOEXEC); if (m_eventFD == -1) { LOG(MemoryPressure, "eventfd() failed: %m"); return; } m_pressureLevelFD = open(s_cgroupMemoryPressureLevel, O_CLOEXEC | O_RDONLY); if (m_pressureLevelFD == -1) { logErrorAndCloseFDs("Failed to open memory.pressure_level"); return; } int fd = open(s_cgroupEventControl, O_CLOEXEC | O_WRONLY); if (fd == -1) { logErrorAndCloseFDs("Failed to open cgroup.event_control"); return; } char line[128] = {0, }; if (snprintf(line, sizeof(line), "%d %d low", m_eventFD, m_pressureLevelFD) < 0 || write(fd, line, strlen(line) + 1) < 0) { logErrorAndCloseFDs("Failed to write cgroup.event_control"); close(fd); return; } close(fd); m_threadID = createThread(waitForMemoryPressureEvent, this, "WebCore: MemoryPressureHandler"); if (!m_threadID) { logErrorAndCloseFDs("Failed to create a thread for MemoryPressureHandler"); return; } if (ReliefLogger::loggingEnabled() && isUnderMemoryPressure()) LOG(MemoryPressure, "System is no longer under memory pressure."); setUnderMemoryPressure(false); m_installed = true; }