2017-08-07 97 views
1

从文件读取数据时遇到内存泄漏。此代码创建了泄漏:从文件句柄读取数据在Linux上泄漏内存

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:运行这段代码,你将不得不在你的工作目录下的“content.pdf”文件。

如果我使用Swift 3.1.1(或3.1)在Linux上运行此代码,它会执行一些循环的迭代,消耗越来越多的内存,直到进程终止。

在Mac上,这也会发生,因为数据被放入Autorelease池中,我可以通过将每次迭代包装在autorelease池中来解决内存问题,但是这在Linux中不存在,所以我不知道如何才能释放增加内存。有人有想法吗?

+0

请看看swift用户邮件列表:希望这可以提供帮助。 https://lists.swift.org/pipermail/swift-users/Week-of-Mon-20161031/003823.html –

+0

@LuisRamirez是的谢谢你,我发现之前已经发布。自动发布池不用于FileHandle/Data的Linux实现。最后,我深入研究了corelibs代码,发现了最终的错误,并提出了一种解决方法(以下回答)。 – drewag

回答

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上,我创建了一个数据副本,在释放时实际上释放底层数据。

0

而不是如何解决缺少的autorelease池,一个更好的问题是如何防止泄漏。也许创建(而不是释放)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") 
} 

这可能不是问题,但它仍然是良好的代码卫生。在坠毁之前有多少个循环?

+0

文件句柄在释放时关闭自己。它们被释放,因为循环的每次迭代中都有函数退出。为了以防万一,我添加了它并以任何方式在386次迭代后崩溃(我确定这取决于我的环境)。 – drewag