修复内存泄漏问题,优化Blob URL清理机制

This commit is contained in:
2025-09-18 23:48:16 +08:00
parent a4583eb1f0
commit 803cc100be
9 changed files with 1009 additions and 408 deletions

View File

@@ -123,6 +123,10 @@ interface AppState {
addBlob: (blob: Blob) => string;
getBlob: (url: string) => Blob | undefined;
cleanupOldHistory: () => void;
// Blob URL清理操作
revokeBlobUrls: (urls: string[]) => void;
cleanupAllBlobUrls: () => void;
}
export const useAppStore = create<AppState>()(
@@ -314,6 +318,36 @@ export const useAppStore = create<AppState>()(
// 清理旧记录以保持在限制内现在限制为1000条
if (updatedProject.generations.length > 1000) {
// 收集需要释放的Blob URLs
const urlsToRevoke: string[] = [];
const generationsToRemove = updatedProject.generations.slice(0, updatedProject.generations.length - 1000);
generationsToRemove.forEach(gen => {
gen.sourceAssets.forEach(asset => {
if (asset.blobUrl.startsWith('blob:')) {
urlsToRevoke.push(asset.blobUrl);
}
});
gen.outputAssetsBlobUrls.forEach(url => {
if (url.startsWith('blob:')) {
urlsToRevoke.push(url);
}
});
});
// 释放Blob URLs
if (urlsToRevoke.length > 0) {
set((innerState) => {
urlsToRevoke.forEach(url => {
URL.revokeObjectURL(url);
const newBlobStore = new Map(innerState.blobStore);
newBlobStore.delete(url);
innerState = { ...innerState, blobStore: newBlobStore };
});
return innerState;
});
}
// 清理数组
updatedProject.generations.splice(0, updatedProject.generations.length - 1000);
// 同时清理IndexedDB中的旧记录
indexedDBService.cleanupOldRecords(1000).catch(err => {
@@ -409,6 +443,34 @@ export const useAppStore = create<AppState>()(
// 清理旧记录以保持在限制内现在限制为1000条
if (updatedProject.edits.length > 1000) {
// 收集需要释放的Blob URLs
const urlsToRevoke: string[] = [];
const editsToRemove = updatedProject.edits.slice(0, updatedProject.edits.length - 1000);
editsToRemove.forEach(edit => {
if (edit.maskReferenceAssetBlobUrl && edit.maskReferenceAssetBlobUrl.startsWith('blob:')) {
urlsToRevoke.push(edit.maskReferenceAssetBlobUrl);
}
edit.outputAssetsBlobUrls.forEach(url => {
if (url.startsWith('blob:')) {
urlsToRevoke.push(url);
}
});
});
// 释放Blob URLs
if (urlsToRevoke.length > 0) {
set((innerState) => {
urlsToRevoke.forEach(url => {
URL.revokeObjectURL(url);
const newBlobStore = new Map(innerState.blobStore);
newBlobStore.delete(url);
innerState = { ...innerState, blobStore: newBlobStore };
});
return innerState;
});
}
// 清理数组
updatedProject.edits.splice(0, updatedProject.edits.length - 1000);
// 同时清理IndexedDB中的旧记录
indexedDBService.cleanupOldRecords(1000).catch(err => {
@@ -437,16 +499,56 @@ export const useAppStore = create<AppState>()(
const generations = [...state.currentProject.generations];
const edits = [...state.currentProject.edits];
// 收集需要释放的Blob URLs
const urlsToRevoke: string[] = [];
// 如果生成记录超过1000条只保留最新的1000条
if (generations.length > 1000) {
const generationsToRemove = generations.slice(0, generations.length - 1000);
generationsToRemove.forEach(gen => {
gen.sourceAssets.forEach(asset => {
if (asset.blobUrl.startsWith('blob:')) {
urlsToRevoke.push(asset.blobUrl);
}
});
gen.outputAssetsBlobUrls.forEach(url => {
if (url.startsWith('blob:')) {
urlsToRevoke.push(url);
}
});
});
generations.splice(0, generations.length - 1000);
}
// 如果编辑记录超过1000条只保留最新的1000条
if (edits.length > 1000) {
const editsToRemove = edits.slice(0, edits.length - 1000);
editsToRemove.forEach(edit => {
if (edit.maskReferenceAssetBlobUrl && edit.maskReferenceAssetBlobUrl.startsWith('blob:')) {
urlsToRevoke.push(edit.maskReferenceAssetBlobUrl);
}
edit.outputAssetsBlobUrls.forEach(url => {
if (url.startsWith('blob:')) {
urlsToRevoke.push(url);
}
});
});
edits.splice(0, edits.length - 1000);
}
// 释放Blob URLs
if (urlsToRevoke.length > 0) {
set((innerState) => {
urlsToRevoke.forEach(url => {
URL.revokeObjectURL(url);
const newBlobStore = new Map(innerState.blobStore);
newBlobStore.delete(url);
innerState = { ...innerState, blobStore: newBlobStore };
});
return innerState;
});
}
// 同时清理IndexedDB中的旧记录
indexedDBService.cleanupOldRecords(1000).catch(err => {
console.error('清理IndexedDB旧记录失败:', err);
@@ -460,6 +562,27 @@ export const useAppStore = create<AppState>()(
updatedAt: Date.now()
}
};
}),
// 释放指定的Blob URLs
revokeBlobUrls: (urls: string[]) => set((state) => {
urls.forEach(url => {
if (state.blobStore.has(url)) {
URL.revokeObjectURL(url);
const newBlobStore = new Map(state.blobStore);
newBlobStore.delete(url);
state = { ...state, blobStore: newBlobStore };
}
});
return state;
}),
// 释放所有Blob URLs
cleanupAllBlobUrls: () => set((state) => {
state.blobStore.forEach((_, url) => {
URL.revokeObjectURL(url);
});
return { ...state, blobStore: new Map() };
})
}),
{