mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(core): Clear lView from IcuIteratorState when stack is empty to prevent memory leak
If a component template contains an icu expression it is being retained until the next change detection cycle for that template. This results in a net retention of only ever a single copy of the given lView but that creates an opportunity for compounding leaks. Change the icu i18n_icu_container_visitor to free the IcuIteratorState retained lView when the stack is empty so that garbage collection can occur when the view is discarded.
This commit is contained in:
committed by
Kristiyan Kostadinov
parent
bfcaf17005
commit
59e648913c
@@ -15,7 +15,7 @@ import {TIcuContainerNode} from '../interfaces/node';
|
||||
import {RNode} from '../interfaces/renderer_dom';
|
||||
import {LView, TVIEW} from '../interfaces/view';
|
||||
|
||||
interface IcuIteratorState {
|
||||
export interface IcuIteratorState {
|
||||
stack: any[];
|
||||
index: number;
|
||||
lView?: LView;
|
||||
@@ -35,7 +35,7 @@ function enterIcu(state: IcuIteratorState, tIcu: TIcu, lView: LView) {
|
||||
}
|
||||
}
|
||||
|
||||
function icuContainerIteratorNext(state: IcuIteratorState): RNode | null {
|
||||
export function icuContainerIteratorNext(state: IcuIteratorState): RNode | null {
|
||||
if (state.index < state.removes!.length) {
|
||||
const removeOpCode = state.removes![state.index++] as number;
|
||||
ngDevMode && assertNumber(removeOpCode, 'Expecting OpCode number');
|
||||
@@ -54,6 +54,8 @@ function icuContainerIteratorNext(state: IcuIteratorState): RNode | null {
|
||||
}
|
||||
} else {
|
||||
if (state.stack.length === 0) {
|
||||
// Clear the lView reference when iteration completes to allow garbage collection
|
||||
state.lView = undefined;
|
||||
return null;
|
||||
} else {
|
||||
state.removes = state.stack.pop();
|
||||
|
||||
@@ -11,6 +11,10 @@ import {
|
||||
getTranslationForTemplate,
|
||||
i18nStartFirstCreatePass,
|
||||
} from '../../../src/render3/i18n/i18n_parse';
|
||||
import {
|
||||
icuContainerIteratorNext,
|
||||
IcuIteratorState,
|
||||
} from '../../../src/render3/i18n/i18n_icu_container_visitor';
|
||||
import {getTIcu} from '../../../src/render3/i18n/i18n_util';
|
||||
import {TNodeType} from '../../../src/render3/interfaces/node';
|
||||
|
||||
@@ -584,6 +588,23 @@ describe('Runtime i18n', () => {
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should clear lView reference when ICU iteration completes', () => {
|
||||
const state: IcuIteratorState = {
|
||||
stack: [],
|
||||
index: 0,
|
||||
removes: [] as any,
|
||||
lView: [] as any, // Mock lView
|
||||
};
|
||||
|
||||
// Call icuContainerIteratorNext with an empty removes array and empty stack
|
||||
// This simulates the end of iteration
|
||||
const result = icuContainerIteratorNext(state);
|
||||
|
||||
expect(result).toBeNull();
|
||||
// Verify that lView should be cleared to allow garbage collection
|
||||
expect(state.lView).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe(`i18nAttribute`, () => {
|
||||
|
||||
Reference in New Issue
Block a user