2017-08-07 55 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