[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