RESOLVED FIXED Bug 203990
MessageEvent should tell its memory cost to GC
https://bugs.webkit.org/show_bug.cgi?id=203990
Summary MessageEvent should tell its memory cost to GC
Konstantin Kaefer
Reported 2019-11-07 22:14:24 PST
Created attachment 383109 [details] Memory leak with web workers Safari leaks memory when sending objects with `postMessage`. It happens when the message isn't accessed in any way on the receiver's side. As soon as the message object is referenced, even without modifying or actually reading its contents, the memory leak goes away. This applies to both webworkers and MessageChannel. In the attached reproduction HTML pages, memory use grows to several gigabytes in the span of a few seconds. Uncommenting the `message.data;` statement removes the memory leak.
Attachments
Memory leak with web workers (724 bytes, text/html)
2019-11-07 22:14 PST, Konstantin Kaefer
no flags
Memory leak with MessageChannel (603 bytes, text/html)
2019-11-07 22:15 PST, Konstantin Kaefer
no flags
Patch (9.97 KB, patch)
2020-06-03 20:59 PDT, Yusuke Suzuki
no flags
Patch (11.26 KB, patch)
2020-06-03 21:26 PDT, Yusuke Suzuki
no flags
Konstantin Kaefer
Comment 1 2019-11-07 22:15:00 PST
Created attachment 383110 [details] Memory leak with MessageChannel
Konstantin Kaefer
Comment 2 2019-11-07 22:40:18 PST
A similar bug was reported in https://bugs.webkit.org/show_bug.cgi?id=194268. The current bug occurs with or without buffer transfer.
Radar WebKit Bug Importer
Comment 3 2019-11-10 13:08:25 PST
youenn fablet
Comment 4 2020-01-09 06:56:56 PST
Testing the MessageChannel repro page, page consumes up to 8GB of memory, then GC kicks in and it continues over and over. This seems fine, not sure whether we could try to optimise things here. Similarly, the web workers page is not crashing, although it eats up to 30GB of memory until GC happens and memory is down to a few MBs and does not increase anymore. Not exactly sure why this behaviour happens, there might be things we could improve. This does not seem critical though.
Konstantin Kaefer
Comment 5 2020-01-09 07:07:16 PST
Thanks for looking into this. Using 8 GB of memory might work on a desktop machine, but it generally doesn't work well on iOS devices, in particular because they don't have swap space. While I generally don't observe page crashes on desktop Safari, I have seen a few on iOS.
Yusuke Suzuki
Comment 6 2020-06-03 20:59:26 PDT
Mark Lam
Comment 7 2020-06-03 21:18:58 PDT
Comment on attachment 400990 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=400990&action=review LGTM but can you rebase the bindings results? It's a bit difficult to see the changes to the emitted finishCreation()s. It'd be easier to audit it from the bindings test results. Thanks. > Source/WebCore/ChangeLog:3 > + Safari leaks memory of objects send through postMessage /send/sent/? > Source/WebCore/ChangeLog:11 > + it does not tell this memory pressure to GC. This patch adds ReportExtraMemoryCost to MessageEvent.idl and memoryCost I suggest /tell this/communicate/. > Source/WebCore/ChangeLog:17 > + But it can be also created from toJSNewlyCreated for Event through EventFactory. If the latter path is taken, we didn't properly /also created/also be created/ /didn't properly/won't properly/ > Source/WebCore/ChangeLog:19 > + IDL code should follow to this convention. /follow to this/follow this/.
Yusuke Suzuki
Comment 8 2020-06-03 21:22:37 PDT
Comment on attachment 400990 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=400990&action=review >> Source/WebCore/ChangeLog:11 >> + it does not tell this memory pressure to GC. This patch adds ReportExtraMemoryCost to MessageEvent.idl and memoryCost > > I suggest /tell this/communicate/. Fixed. >> Source/WebCore/ChangeLog:17 >> + But it can be also created from toJSNewlyCreated for Event through EventFactory. If the latter path is taken, we didn't properly > > /also created/also be created/ > /didn't properly/won't properly/ Fixed. >> Source/WebCore/ChangeLog:19 >> + IDL code should follow to this convention. > > /follow to this/follow this/. Fixed.
Yusuke Suzuki
Comment 9 2020-06-03 21:26:06 PDT
Mark Lam
Comment 10 2020-06-03 22:00:27 PDT
Comment on attachment 400992 [details] Patch r=me
Konstantin Kaefer
Comment 11 2020-06-04 00:51:25 PDT
It looks like this patch also addresses https://bugs.webkit.org/show_bug.cgi?id=204515 and fixes the exact problem described there.
Yusuke Suzuki
Comment 12 2020-06-04 11:28:03 PDT
(In reply to Konstantin Kaefer from comment #11) > It looks like this patch also addresses > https://bugs.webkit.org/show_bug.cgi?id=204515 and fixes the exact problem > described there. Right! Nice.
Yusuke Suzuki
Comment 13 2020-06-04 12:12:40 PDT
*** Bug 204515 has been marked as a duplicate of this bug. ***
Alex Christensen
Comment 14 2020-06-04 13:42:50 PDT
Comment on attachment 400992 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=400992&action=review > Source/WebCore/bindings/js/SerializedScriptValue.cpp:3640 > + m_memoryCost = computeMemoryCost(); Couldn't this go in the initializers instead of the body of the constructor? Also, wouldn't it be better to compute this lazily, in case it is never requested? > Source/WebCore/bindings/js/SerializedScriptValue.cpp:3647 > + m_memoryCost = computeMemoryCost(); ditto > Source/WebCore/bindings/js/SerializedScriptValue.cpp:3679 > + size_t cost = m_data.size(); Should we add sizeof(*this)?
Yusuke Suzuki
Comment 15 2020-06-04 14:12:01 PDT
Comment on attachment 400992 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=400992&action=review >> Source/WebCore/bindings/js/SerializedScriptValue.cpp:3640 >> + m_memoryCost = computeMemoryCost(); > > Couldn't this go in the initializers instead of the body of the constructor? > Also, wouldn't it be better to compute this lazily, in case it is never requested? Lazy calculation does not work because this memoryCost function can be called concurrently from GC concurrent marking thread, and SerializedScriptValue's fields can be sometimes nulled after the creation. If we call this function lazily, this can cause GC crash. I think the current form is better since it is consistent to the other constructors. For example, in `SerializedScriptValue::SerializedScriptValue(Vector<uint8_t>&& buffer, const Vector ...)` constructor, we cannot call this function as a part of initializers since the body is calculating fields that computeMemoryCost relies on. Since computeMemoryCost() depends on the other fields of SerializedScriptValue, putting this in body can prevent a bug like this. SerializedScriptValue::SerializedScriptValue(Vector<uint8_t>&& buffer, std::unique_ptr<ArrayBufferContentsArray> arrayBufferContentsArray) : m_data(WTFMove(buffer)) , m_arrayBufferContentsArray(WTFMove(arrayBufferContentsArray)) , m_memoryCost(computeMemoryCost()) { } And if some change reorders fields, SerializedScriptValue::SerializedScriptValue(Vector<uint8_t>&& buffer, std::unique_ptr<ArrayBufferContentsArray> arrayBufferContentsArray) : m_data(WTFMove(buffer)) , m_memoryCost(computeMemoryCost()) , m_arrayBufferContentsArray(WTFMove(arrayBufferContentsArray)) { } this leads to a bug. >> Source/WebCore/bindings/js/SerializedScriptValue.cpp:3679 >> + size_t cost = m_data.size(); > > Should we add sizeof(*this)? It does not need to take it. For example, StringImpl::costDuringGC is not adding sizeof(StringImpl) to the result. memoryCost function does not need to be precise. The goal of this function is getting rough estimated size and reporting it to GC as a memory-pressure, and the critical part of this function is that we should report super-large-sized cost.
EWS
Comment 16 2020-06-04 15:36:17 PDT
Committed r262581: <https://trac.webkit.org/changeset/262581> All reviewed patches have been landed. Closing bug and clearing flags on attachment 400992 [details].
Note You need to log in before you can comment on or make changes to this bug.