从文件读取数据时遇到内存泄漏。此代码创建泄漏:
func read() throws {
let url = URL(fileURLWithPath: "content.pdf")
let fileHandle = try FileHandle(forReadingFrom: url)
while true {
let chunk = fileHandle.readData(ofLength: 256)
guard !chunk.isEmpty else {
break
}
}
print("read")
}
do {
for _ in 0 ..< 10000 {
try read()
}
}
catch {
print("Error: \(error)")
}
* FYI:要运行此代码,您必须拥有&#34; content.pdf&#34;文件在你的工作目录中。
如果我使用Swift 3.1.1(或3.1)在Linux上运行它,它会循环多次迭代,消耗越来越多的内存,直到进程被终止。
在Mac上也会发生这种情况,因为数据被放入自动释放池中,我可以通过将每次迭代包装在自动释放池中来解决内存问题,但这在Linux上不存在,所以我不知道我怎么能释放那段记忆。有没有人有想法?
答案 0 :(得分:3)
我发现了标准库中的问题。实际上已经开放bug report了。基本上问题是readData(ofLength :)方法返回一个Data对象,该对象在解除分配后不会自行清理。
目前,我正在使用此解决方法:
extension FileHandle {
public func safelyReadData(ofLength length: Int) -> Data {
#if os(Linux)
var leakingData = self.readData(ofLength: length)
var data: Data = Data()
if leakingData.count > 0 {
leakingData.withUnsafeMutableBytes({ (bytes: UnsafeMutablePointer<UInt8>) -> Void in
data = Data(bytesNoCopy: bytes, count: leakingData.count, deallocator: .free)
})
}
return data
#else
return self.readData(ofLength: length)
#endif
}
}
我之前使用的任何地方readData(ofLength:)
我现在正在使用safelyReadData(ofLength:)
方法。在除Linux以外的所有平台上,它只是调用原始版本,因为这些实现很好。在Linux上,我正在创建一个数据副本,在释放时实际释放基础数据。
答案 1 :(得分:0)
而不是如何解决丢失的自动释放池,更好的问题是如何防止泄漏。也许创建(而不是解除分配)10,000个FileHandles是个问题。试试这个。
func read() throws {
let url = URL(fileURLWithPath: "content.pdf")
let fileHandle = try FileHandle(forReadingFrom: url)
while true {
let chunk = fileHandle.readData(ofLength: 256)
guard !chunk.isEmpty else {
break
}
}
fileHandle.closeFile()
print("read")
}
这可能不是问题,但它仍然是良好的代码卫生。在崩溃之前制作了多少个循环?