質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

0回答

548閲覧

バイナリデータを解析中に発生するメモリリークの原因をつきとめたい

shatiatan

総合スコア0

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

1クリップ

投稿2022/09/06 05:45

編集2022/09/06 06:20

前提

pngファイルのバイナリ(16進数)から指定した2つの文字列を探し、
その文字列にはさまれた文字列を取得する、という処理を実装しました。
しかし、ファイル数・ファイルサイズが膨大になると、処理途中で以下のエラーが発生します。

実現したいこと

  1. 任意のディレクトリにあるpngファイルのバイナリを取得
  2. 取得したバイナリ上にAAAXXXCCC という文字列があるとする(XXXは文字数も含めファイルによって変わるため不明、AAA,CCCは固定)
  3. XXXを知りたいので、AAAとCCCを探し、位置を取得する
  4. AAAの末尾から、CCCの先頭の間にある文字列を取得する(この場合はXXX)
  5. 1~4をファイル数だけ繰り返す。
  6. 番号リスト

発生している問題・エラーメッセージ

以下のエラーが発生します。 OutOfMemoryException

該当のソースコード

static private byte[] bytes = new byte[300000000] /* 300MBのファイルまで対応 */ private async void ButtonExecution_Click_1(object sender, EventArgs e) { string[] l_file_dir; /* pngファイルのディレクトリを取得 */ l_file_dir = Directory.GetFiles(textBox1.Text, "*.png", SearchOption.AllDirectories); /* ファイル選別処理を実行*/ await Task.Run(() => GetFileInfo_inf(l_file_dir)); } private void GetFileInfo_inf(string[] path) { int XXX; int l_length; int l_start = 0; int l_end = 0; using (FileStream fs = File.OpenRead(path[num])) { // バイナリファイルの読込 先頭(0)から末尾(fs.Length)まで fs.Read(bytes, 0, (int)fs.Length); l_length = (int)fs.Length; } for (int i = 0; i < l_length; i++) { /* まずAAAの位置を取得 */ if (0 == l_start) { /* AAA のバイナリ */ if (0x64 == bytes[i] && 0x75 == bytes[i + 1] && 0x72 == bytes[i + 2] && 0x61 == bytes[i + 3] && 0x74 == bytes[i + 4] && 0x69 == bytes[i + 5] ) { /* AAAの終了位置(XXXの開始位置)を取得 */ l_start = i + 6; } } /* AAAの位置がわかったらCCCの位置を取得 */ else { /* CCC のバイナリ */ if (0x22 == bytes[i] && 0x20 == bytes[i + 1] && 0x62 == bytes[i + 2] && 0x6C == bytes[i + 3] ) { /* CCCの開始位置(XXXの終了位置)を取得 */ l_end = i - 1; break; } } } /* AAA ~ CCC の間がXXXに該当する */ l_time_len = l_end - l_start + 1; /* バイナリファイルからXXXを取得する */ byte[] l_time_len_arr = new byte[l_time_len]; Array.Copy(bytes, l_start, l_time_len_arr, 0, l_time_len); /* Shift_JISに変換 */ System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance); l_time_len = Encoding.GetEncoding("Shift_JIS").GetString(l_time_len_arr); MessageBox.Show(l_time_len); }

試したこと

もともと、バイナリをバイト配列に代入することを関数内で行っておりメモリリークが発生していました。
静的領域に大きな単位で確保するように変更した結果、メモリが増え続けるエラーは減りましたが、
ファイルが多くなると再びエラーが出てしまう状況です。

見よう見まねでC#の勉強を始めたため稚拙なコードかと思いますが、よろしければアドバイスいただきたいです。よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Zuishin

2022/09/06 05:54 編集

このコード本当に動きますか? 何か別の物を走らせているのでは? l_start が見るからにスコープ外からアクセスされているので、動くとしたらどこか別の場所でも定義されているのでは?
sazi

2022/09/06 09:17 編集

データが大量で想定外のデータが含まれる事によるロジックの問題という事も考えられますので、特定のデータで発生していないかも確認された方がよろしいかと。
dodox86

2022/09/06 06:52

> ファイル数・ファイルサイズが膨大になると、処理途中で以下のエラーが発生します。 非同期に開けるだけファイルを開いて見た目で並列に処理してたらいずれそうなると思います。1バイトずつ見てるし。見よう見真似でやるのではなく、理解してやりましょう。最初は普通に同期式、逐次で処理したらいかがですか。
KOZ6.0

2022/09/06 08:46 編集

dodox86 さんのコメントを見て気になりました。まさか並列処理していないでしょうね? というかボタンをポチポチすると並列処理されちゃいますね。byes が複数のスレッドから使われるのでまずいです。
shatiatan

2022/09/06 08:56

@Zuishin さん ご指摘ありがとうございます。ソースコードに誤植がありました。失礼いたしました。 @sazi さん ご指摘ありがとうございます。思い当たる節があります。エラーが出るファイルのバイナリで確認してみます。 @dodox86 さん ご指摘ありがとうございます。もともと逐次処理で記述していたのですが、プログレスバーが固まってしまうため、処理ごと並行処理に変更した経緯がありました。一旦逐次処理にもどし、同じエラーが出るか動作確認をしてみます。 @KOZ6.0 さん ご指摘ありがとうございます。文字列がない場合は、このループには入らない処理にしています。メソッドの紹介、大変助かります。ありがとうございます。並列処理はこの関数以外では行っていません。プログレスバーが応答なしになるのを回避するためだけのために、async/awaitを使って処理完了まで待つ。ようにしています。
KOZ6.0

2022/09/06 09:30

> プログレスバーが応答なしになるのを回避するためだけのために、async/awaitを使って処理完了まで待つ。ようにしています 処理中はボタンを押せないようにする等、並列処理を抑止する処理が入っていれば大丈夫です。
Zuishin

2022/09/06 13:32

ディレクトリを検索して見つかったファイルを全部読み込んでから処理しようとしていますね。 一つずつしないと、大量のファイルがあった場合にメモリが足りなくなると思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問