<https://webkit.org/b/158941>
Reviewed by Antti Koivisto.
A resource is "live" if it's currently in use by a web page, and "dead" if it's
only kept alive by the memory cache.
This patch adds a mechanism that looks at CachedImage resources to see if all the
clients that make them appear "live" are actually pages in the page cache.
If so, we let the "jettison expensive objects on top-level navigation" mechanism
discard the decoded data for such half-live images. This can reduce the peak
memory usage during navigations quite a bit.
* loader/FrameLoader.cpp:
(WebCore::FrameLoader::commitProvisionalLoad): Move the call to MemoryPressureHandler
before we add the outgoing page to the page cache. This allows the jettisoning code
to make decisions based on which pages were cached *before* the navigation.
* loader/cache/CachedImageClient.h:
(WebCore::CachedImageClient::inPageCache):
* loader/ImageLoader.h:
* loader/ImageLoader.cpp:
(WebCore::ImageLoader::inPageCache):
* rendering/RenderObject.h:
(WebCore::RenderObject::inPageCache): Added a CachedImageClient::inPageCache() virtual
to determine which clients are currently in page cache (answered by their Document.)
* loader/cache/CachedImage.h:
* loader/cache/CachedImage.cpp:
(WebCore::CachedImage::areAllClientsInPageCache): Walks all CachedImageClient clients
and returns true if all of them are inPageCache().
* platform/MemoryPressureHandler.cpp:
(WebCore::MemoryPressureHandler::jettisonExpensiveObjectsOnTopLevelNavigation):
Walk all the known CachedImages and nuke decoded data for those that have some but
are only considered live due to clients in the page cache.
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@202231
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2016-06-20 Andreas Kling <akling@apple.com>
+
+ When navigating, discard decoded image data that is only live due to page cache.
+ <https://webkit.org/b/158941>
+
+ Reviewed by Antti Koivisto.
+
+ A resource is "live" if it's currently in use by a web page, and "dead" if it's
+ only kept alive by the memory cache.
+
+ This patch adds a mechanism that looks at CachedImage resources to see if all the
+ clients that make them appear "live" are actually pages in the page cache.
+
+ If so, we let the "jettison expensive objects on top-level navigation" mechanism
+ discard the decoded data for such half-live images. This can reduce the peak
+ memory usage during navigations quite a bit.
+
+ * loader/FrameLoader.cpp:
+ (WebCore::FrameLoader::commitProvisionalLoad): Move the call to MemoryPressureHandler
+ before we add the outgoing page to the page cache. This allows the jettisoning code
+ to make decisions based on which pages were cached *before* the navigation.
+
+ * loader/cache/CachedImageClient.h:
+ (WebCore::CachedImageClient::inPageCache):
+ * loader/ImageLoader.h:
+ * loader/ImageLoader.cpp:
+ (WebCore::ImageLoader::inPageCache):
+ * rendering/RenderObject.h:
+ (WebCore::RenderObject::inPageCache): Added a CachedImageClient::inPageCache() virtual
+ to determine which clients are currently in page cache (answered by their Document.)
+
+ * loader/cache/CachedImage.h:
+ * loader/cache/CachedImage.cpp:
+ (WebCore::CachedImage::areAllClientsInPageCache): Walks all CachedImageClient clients
+ and returns true if all of them are inPageCache().
+
+ * platform/MemoryPressureHandler.cpp:
+ (WebCore::MemoryPressureHandler::jettisonExpensiveObjectsOnTopLevelNavigation):
+ Walk all the known CachedImages and nuke decoded data for those that have some but
+ are only considered live due to clients in the page cache.
+
2016-06-20 Chris Dumez <cdumez@apple.com>
Unreviewed, fix post-landing review comment from Darin on r202188.
willTransitionToCommitted();
if (!m_frame.tree().parent() && history().currentItem()) {
+ MemoryPressureHandler::singleton().jettisonExpensiveObjectsOnTopLevelNavigation();
+
// Check to see if we need to cache the page we are navigating away from into the back/forward cache.
// We are doing this here because we know for sure that a new page is about to be loaded.
PageCache::singleton().addIfCacheable(*history().currentItem(), m_frame.page());
-
- MemoryPressureHandler::singleton().jettisonExpensiveObjectsOnTopLevelNavigation();
}
if (m_loadType != FrameLoadType::Replace)
m_failedLoadURL = AtomicString();
}
+bool ImageLoader::inPageCache() const
+{
+ return m_element.document().inPageCache();
+}
+
}
virtual void dispatchLoadEvent() = 0;
virtual String sourceURI(const AtomicString&) const = 0;
+ bool inPageCache() const final;
+
void updatedHasPendingEvent();
void dispatchPendingBeforeLoadEvent();
return CachedResource::makeRevalidationDecision(cachePolicy);
}
+bool CachedImage::areAllClientsInPageCache() const
+{
+ for (auto& entry : m_clients) {
+ auto& client = *entry.key;
+ if (client.resourceClientType() != CachedImageClient::expectedType())
+ continue;
+ if (!static_cast<CachedImageClient&>(client).inPageCache())
+ return false;
+ }
+ return true;
+}
+
} // namespace WebCore
void addDataBuffer(SharedBuffer&) override;
void finishLoading(SharedBuffer*) override;
+ bool areAllClientsInPageCache() const;
+
enum SizeType {
UsedSize,
IntrinsicSize
// Called when GIF animation progresses.
virtual void newImageAnimationFrameAvailable(CachedImage& image) { imageChanged(&image); }
+
+ virtual bool inPageCache() const { return false; }
};
}
/*
- * Copyright (C) 2011, 2014 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2011-2016 Apple Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
#include "MemoryPressureHandler.h"
#include "CSSValuePool.h"
+#include "CachedImage.h"
#include "Chrome.h"
#include "ChromeClient.h"
#include "Document.h"
void MemoryPressureHandler::jettisonExpensiveObjectsOnTopLevelNavigation()
{
-#if PLATFORM(IOS)
// Protect against doing excessive jettisoning during repeated navigations.
const auto minimumTimeSinceNavigation = 2s;
if (!shouldJettison)
return;
+ MemoryCache::singleton().forEachResource([](CachedResource& resource) {
+ if (resource.isImage()
+ && resource.decodedSize()
+ && downcast<CachedImage>(resource).areAllClientsInPageCache()) {
+ resource.destroyDecodedData();
+ }
+ });
+
+#if PLATFORM(IOS)
// Throw away linked JS code. Linked code is tied to a global object and is not reusable.
// The immediate memory savings outweigh the cost of recompilation in case we go back again.
GCController::singleton().deleteAllLinkedCode();
void setNeedsLayoutIsForbidden(bool flag) { m_setNeedsLayoutForbidden = flag; }
#endif
+ bool inPageCache() const final { return document().inPageCache(); }
+
void addAbsoluteRectForLayer(LayoutRect& result);
void setLayerNeedsFullRepaint();
void setLayerNeedsFullRepaintForPositionedMovementLayout();