[Webkit-unassigned] [Bug 241750] New: FileReader.readAsArrayBuffer() Leaks Page Memory

bugzilla-daemon at webkit.org bugzilla-daemon at webkit.org
Sat Jun 18 07:03:19 PDT 2022


https://bugs.webkit.org/show_bug.cgi?id=241750

            Bug ID: 241750
           Summary: FileReader.readAsArrayBuffer() Leaks Page Memory
           Product: WebKit
           Version: Safari 15
          Hardware: iPhone / iPad
                OS: iOS 15
            Status: NEW
          Severity: Major
          Priority: P2
         Component: New Bugs
          Assignee: webkit-unassigned at lists.webkit.org
          Reporter: dmitry.sharygin at gmail.com

Created attachment 460329

  --> https://bugs.webkit.org/attachment.cgi?id=460329&action=review

Page Memory Timeline

Also posted on StackOverflow: https://stackoverflow.com/questions/72663368/filereader-page-memory-leak

We are building a React SPA. The app allows you to submit a form with photos many times in a row. We started stress testing the app and realized that it stops working on iOS (iPad Mini) after several dozens of file uploads. Profiled the memory in Safari and found that what WebKit calls Page Memory (https://webkit.org/blog/6425/memory-debugging-with-web-inspector/) keeps climbing whenever we use FileReader.readAsArrayBuffer, but it never seems to be released. At some point, the App becomes very slow (it never crashes, but connections to local IndexDB start to drop). This is also reproduceable on MacOS (M1) Safari 15.4 and Technology Preview.

Here is the barebones application created with create-react-app:

import FileReaderHandler from './FileReaderHandler';
import { useRef } from 'react';

function App() {
    const fileReaderHandler = useRef(new FileReaderHandler());

    const onSelectFiles = async (event) => {
        event.stopPropagation();
        event.preventDefault();
        const files = Array.from(event.target.files);
        for (const file of files) {
            await fileReaderHandler.current.readFile(file);
        }
    }

    return (
        <div>
            <input multiple type="file" accept="image/*"
                onChange={event => onSelectFiles(event)}
                onClick={event => event.target.value = null}
            />
        </div>
    );
}

export default App;


Here is the FileReaderHandler implementation:

export default class FileReaderHandler {
    async readFile(file, fileReader) {
        return new Promise((resolve, reject) => {
            const fileReader = new FileReader();

            function resolveHandler () {
                cleanHandlers()
                resolve(fileReader.result)
            }

            function cleanHandlers () {
                fileReader.removeEventListener("loadend", resolveHandler);
            }

            fileReader.addEventListener("loadend", resolveHandler);

            fileReader.readAsArrayBuffer(file);
        })
    }
}


See attached for the screenshot of Web Inspector showing Page Memory growing as I continuously select and read the same 10 image files.

t makes no difference if the code handling the reading is in the component itself or in the class. I also tried using a singleton FileReader passed into the function from component, without much luck. Read online that one possible reason memory leaks can occur is due to not cleaning up event handlers, but as you can see in the code above - I do remove the listener.

I tried a version of this code without the Promise just in case the anonymous function somehow holds on to the references, but also no luck.

Finally, I also tried URL.createObjectURL/revokeObjectURL, and it has a similar effect on memory.

Refreshing the page is not an option, because the app needs to work offline and there are background processes running.

>From what I understand from the WebKit link above, the memory is not on Heap, but rather some internal cache. Can someone with better understanding of WebKit memory management explain what is happening and how to prevent this? This is actually blocking our release.

-- 
You are receiving this mail because:
You are the assignee for the bug.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.webkit.org/pipermail/webkit-unassigned/attachments/20220618/d3e0fb32/attachment.htm>


More information about the webkit-unassigned mailing list