Angular:并发下载后的内存泄漏?

我正在开发一个Angular应用程序,我创建了一个这样的下载页面:

《Angular:并发下载后的内存泄漏?》

如果我查看Chrome任务管理器,我会发现与我的应用程序相关的进程会保留大约130 MB的内存.

然后,用户可以单击每个下载按钮启动同时下载(总文件大小为266 MB):

《Angular:并发下载后的内存泄漏?》

现在,如果我再次检查任务管理器,我会看到内存使用量增加,当所有下载完成后,其峰值大约为650 MB.
我注意到一个奇怪的事情:内存的最终用法似乎是以下公式的总和:

初始内存使用量2 *总文件大小=最终内存使用量

130 MB 2 * 266 MB = 130 MB 532 MB = 662 MB = ~650 MB.

如果我尝试使用Chrome垃圾收集器,它会减少大约30 MB,但我仍然拥有620 MB作为内存使用量的应用程序.
那么,我该如何解决这个问题呢?你能看到我的代码有什么问题吗?
(我试图在Component的ngOnDestroy上使用取消订阅,但它没有用)

这是当我单击文件的下载按钮(从下载页面)时调用的函数:

getFile(file: any): void {
    this.fileDownloadService.downloadFile(file).subscribe(
        (fileinfo: SispFile) => {
            file.downloading = true;
        },
        (err: any) => {
            file.downloading = false;
            console.log('errore', err);
        },
        () => {
            file.downloading = false;
            console.log('completated');
        });
}

这是FileDownloadService中的downloadFile函数,由getFile调用:

downloadFile(file: SispFile): Observable<any> {
    let conf: any = {};
    conf.totalChunks = Math.max(Math.ceil(file.size / this.chunkSizeDownload), 1);
    conf.mime = file.extension;
    conf.currentChunk = 0;
    conf.byteDownloaded = 0;
    conf.chunkSizeDownload = this.chunkSizeDownload;
    conf.chunkBlobs = [];
    conf.finalBlob = null;

    file.progress = 0;

    return new Observable(observer => {
        observer.next(file);
        this.addChunk(file, observer, conf);
    })
}

addChunk(file: SispFile, observer: any, conf: any) {
    if (conf.currentChunk == conf.totalChunks) {
        observer.complete();
        conf.finalBlob = new Blob(conf.chunkBlobs, {type: conf.mime});

        let fileURL = URL.createObjectURL(conf.finalBlob);
        let a = document.createElement("a");
        a.href = fileURL;
        a.download = file.name;
        document.body.appendChild(a);
        a.click();
        window.URL.revokeObjectURL(fileURL);
    } else {
        this.docService.downloadFile(file, conf.chunkSizeDownload, conf.byteDownloaded)
            .then(response => {
                let typedArray = this.createBlob(response['_body'], conf.mime);
                conf.chunkBlobs[conf.currentChunk] = typedArray;
                conf.currentChunk++;
                conf.byteDownloaded = conf.currentChunk * conf.chunkSizeDownload;
                if (conf.Downloaded + this.chunkSizeDownload > file.size) {
                    conf.chunkSizeDownload = file.size - conf.Downloaded + 1;
                }

                let progress = Math.round((conf.currentChunk * 100) / conf.totalChunks);
                file.progress = progress;
                observer.next(file);
                this.addChunk(file, observer, conf);
            })
            .catch((error: ErrorMessage) => {
                observer.error(error);
                console.log('Errore server: ' + error);
            });
    }
}

这是对DocService中后端端点的最终调用:

downloadFile(file: SispFile, length: number, offset: number) {
    let url = Util.format(this.downloadFileUrl, {id: file.id});
    let body: any = { "downloadMode": "PAYLOAD", "fileId": file.id, "length": length, "offset": offset };
    return this.http.post(url, body)
        .toPromise()
        .then(response => {
            return response;
        })
        .catch(this.handleError);
}

最佳答案 我发现Angular有这个
Testability类.它的定义是:

The Testability service provides testing hooks that can be accessed
from the browser and by services such as Protractor. Each bootstrapped
Angular application on the page will have an instance of Testability.

您可以通过TestabilityRegistry访问特定元素的Testability实例的全局注册表.

正如我们在this issue中所看到的,销毁组件不会释放浏览器的内存,因为Testability API不会释放它的引用.

所以,也许,通过清除TestabilityRegistry on destroy可以帮助你摆脱那些总结导致你的620MB内存泄漏的引用.

这是我发现的the fix

我希望这有助于解决您的问题,或者至少我希望我能够设法给您一个不同的观点或新的研究领域!

更新:

从我所看到的,为了清理TestabilityRegistry:

在modules/@angular/core/src/testability/testability.ts中:

destroy(): void {
  this._applications.clear();
  testabilityGetter.onDestroy();
}

这将是破坏方法.

在modules/@angular/core/src/application_ref.ts中:

this._injector.get(TestabilityRegistry).destroy();

我们调用destroy方法.

在modules/@angular/platform-b​​rowser/src/browser/testability.ts中:

onDestroy(): void {
   delete global.getAngularTestability;
   delete global.getAllAngularTestabilities;
   delete global.getAllAngularRootElements;
   delete global.frameworkStabilizers;
}

在销毁时,我们确保删除可测试性实例.

看看this commit.它向您展示了如何清理TestabilityRegistry.

点赞