我正在尝试迭代大型文件树以读取 URL 并创建哈希值,但存在无法修复的内存泄漏。消耗的内存不断增加,直到应用程序停止工作。出于测试原因,我尝试读取 600Gb/350T 文件并对其进行哈希处理,其中单个文件高达 25Gb,将 URL 和哈希值写入一个简单的文本文件。生产数据约为 50-70Tb/180 万个文件。
我读了很多文章和帖子,了解了“autoreleasepool”,但到目前为止没有任何效果。散列文件所消耗的内存没有被释放,并且应用程序内存堆栈快速增长。
func readWrite(_ url: URL) {
let enumerator = FileManager().enumerator(at: url, includingPropertiesForKeys: [.isRegularFileKey])
while let element = enumerator?.nextObject() as? URL {
do {
if element.isFileURL {
let data = try Data(contentsOf: element)
let hash = createFileHash(element)
let stringData = Data("\(hash),\(element.description)\n".utf8)
write(stringData, url)
}
} catch {
print("\(error.localizedDescription)")
}
}
}
private func createFileHash(_ url: URL) -> Int {
autoreleasepool {
guard let data = try? Data(contentsOf: url) else { return 0 }
return data.hashValue
}
}
您对
autoreleasepool
的使用成功释放了您在 Data(contentsOf: url)
中创建的 createFileHash
。但是,您的 while
循环中还有两行创建 Data
对象:
let data = try Data(contentsOf: element)
// and
let stringData = Data("\(hash),\(element.description)\n".utf8)
第一行创建的
Data
没有被释放。您似乎也没有使用这个 data
变量,所以您可以删除这一行。对于这个答案的其余部分,我将假设这不是您的完整代码,并且您do在某个地方使用它。
第二行创建的
Data
完全是一个 Swift 对象,所以据我所知,它会像所有其他 Swift 对象一样被正常释放。
您应该用
autoreleasepool
包裹整个while循环体,并且可以删除autoreleasepool
中的createFileHash
(假设这是您调用createFileHash
的唯一地方)。
while let element = enumerator?.nextObject() as? URL {
autoreleasepool {
do {
if element.isFileURL {
let data = try Data(contentsOf: element)
let hash = createFileHash(element)
let stringData = Data("\(hash),\(element.description)\n".utf8)
write(stringData, url)
}
} catch {
print("\(error.localizedDescription)")
}
}
}