2017-08-07 4 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」ファイルを持っている必要がありますこのコードを実行するには、このコードでは、リークを作成します。

これをLinux上でSwift 3.1.1(または3.1)と一緒に実行すると、プロセスが終了するまでループの繰り返しが多く消費されます。

これは、データがAutoreleaseプールに入れられているために発生します。自動解放プールで各繰り返しをラップすることでメモリの問題を解決できますが、Linuxには存在しません。そのメモリをアップする。誰にもアイデアはありますか?

+0

迅速なユーザーメールリストをご覧ください。うまくいけば、これが役に立ちます。 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 
    } 
} 

どこでも私は以前、私は今、私のsafelyReadData(ofLength:)方法を使用していますreadData(ofLength:)を使用していました。 Linux以外のすべてのプラットフォームでは、それらの実装がうまくいくので、単にオリジナルを呼び出します。 Linuxでは、割り当て解除時に実際に基礎となるデータを解放するデータのコピーを作成しています。

0

欠落している自動解放プールを回避する方法の代わりに、より良い質問は、漏れを防ぐ方法です。おそらく、10,000のFileHandleを作成する(そして割り当てを解除しない)ことは問題です。これを試して。

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

関連する問題