@fuzigiwa2 (義将 藤極)

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

List<IMultipartFormSection>を設定したUnityWebRequest.uploadHandlerのメモリリーク

Listを設定したUnityWebRequest.uploadHandlerのメモリリーク

Unityで開発を行っております。
環境はUnity 2018.4.22f1になります。
ファイルをアップロードするために以下のコードを作成しました。

public IEnumerator mediaUpload()
{
    // /media
    string l_url = _urlMain + "/media";

    // リソースをアンロードする.
    System.GC.Collect();
    Resources.UnloadUnusedAssets();
    yield return null;

    string _path = l_save1_path = UnityEngine.Application.persistentDataPath + "/" + UnityEngine.Application.productName + "/" + "updata.mp4";

    // ファイル読み込み
    byte[] l_file_data = File.ReadAllBytes( _path );
    string l_fileName = "hoge.mp4";

    var request = new UnityWebRequest( l_url, "POST");
    setRequest( request );
    // タイムアウト設定
    request.timeout = 300;

    List<IMultipartFormSection> requestData = new List<IMultipartFormSection>();
    requestData.Add( new MultipartFormFileSection( "file", l_file_data, l_fileName, "video/*" ) );

    if( 0 < user_id )
        requestData.Add( new MultipartFormDataSection( "user_id",               user_id.ToString() ) );
    if( 0 < player_id )
        requestData.Add( new MultipartFormDataSection( "player_id",             player_id.ToString() ) );
    if( 0 < group_id )
        requestData.Add( new MultipartFormDataSection( "group_id",              group_id.ToString() ) );
    if( 0 < team_id )
        requestData.Add( new MultipartFormDataSection( "team_id",               team_id.ToString() ) );
    if( 0 < game_id )
        requestData.Add( new MultipartFormDataSection( "game_id",               game_id.ToString() ) );

    if( !title.Equals( "" ) )
        requestData.Add( new MultipartFormDataSection( "title",                 System.Text.Encoding.UTF8.GetBytes( title ) ) );
    if( !comment.Equals( "" ) )
        requestData.Add( new MultipartFormDataSection( "comment",               System.Text.Encoding.UTF8.GetBytes( comment ) ) );

    byte[] boundary = UnityWebRequest.GenerateBoundary();
    byte[] formSections = UnityWebRequest.SerializeFormSections( requestData, boundary );

    // データ設定
    request.uploadHandler   = (UploadHandler) new UploadHandlerRaw( formSections );
    request.downloadHandler = (DownloadHandler) new DownloadHandlerBuffer();

    // ヘッダを設定
    request.SetRequestHeader("X-Requested-With", "XMLHttpRequest" );
    request.SetRequestHeader("Content-Type", "multipart/form-data; boundary="+System.Text.Encoding.UTF8.GetString(boundary) );
    request.SetRequestHeader( "Authorization", "Bearer "+ _access_token );
    // アプリ名、Ver、プラットフォーム
    request.SetRequestHeader("X-Escore-Env", "app="+APP_NAME+"; ver="+VER+"; os="+isOS() );

    yield return request.Send();

    // 通信エラーチェック
    if(request.isNetworkError){
        Debug.Log("mediaUpload()::Error = " + request.error );
        // UTF8文字列として取得する
        string l_file = request.downloadHandler.text;
        Debug.Log("mediaUpload()::l_file = "+l_file );
    }else{
        Debug.Log("mediaUpload()::complete!---------------------->>>>");

        // UTF8文字列として取得する
        string l_file = request.downloadHandler.text;
        Debug.Log("mediaUpload()::l_file = "+l_file );
    }
}

このコードで120MBほどのファイルをアップロードする際にWindowsエディター上ではメモリの異常増加は見られなかったのですが、
iPad上で確認した際にメモリが800MB以上増えメモリリークが発生しました。

ファイル自体は120MBですが、なぜここまで使用メモリーが増えるのでしょうか。
こちら改善方法をご存じでしょうか。
UnityのVerアップも行い、2020.3.1f1にしましたが改善されませんでした。

よろしくお願い致します。

0 likes

2Answer

ソースコードが部分的ですので、確かなことは申し上げられませんが、気が付いた範囲で書かせていただきます。

  • ファイルを読み込んだ生データl_file_data は、MultipartFormFileSectionクラスのコンストラクタに渡されて、生成されたインスタンスで保持されます。
    • 同インスタンスは、生データとは別に、エンコードされた状態のデータも保持するものと思われます。
      • エンコード後のデータは、生データの2~数倍になるものと考えられます。
    • 同インスタンスは、List<IMultipartFormSection> requestDataに、他のフォームリクエストデータとともに保持されます。
  • yield returnがあることから、一連のコードは、コルーチンまたは同種の非同期実行されるメソッドの一部と考えられます。
    • その結果、保持された全てのデータは、POST処理が完了するまで、メモリを占有するものと考えられます。
    • POST処理完了後も、ガベージコレクタに回収されるまでは、チェックされた指標によっては、メモリを消費していることになるのではないでしょうか。

見える範囲内では、メモリーリークではなく既定の挙動のように思われます。
何か、メモリリークと判断された根拠があればお示しください。
また、周囲のソースコードがあれば、分析の助けになるだろうと思います。

1Like

Comments

  1. > 作成したコルーチン処理として編集いたしました。
    > l_file_dataは参照されているだけであってファイルのバイト数分のメモリ使用しかされていないと考えております。これはWindowsエディタ上のメモリをウォッチしたところ大きな増加がみられなかったこととXcode上で「exc_resource resource_type_memory (limit=1850 mb unused=0x0)」が発生したことからメモリリークと判断しております。

    エラーコード情報をお知らせいただき、また、ソースコードの追加もいただきまして、どうもありがとうございます。
    何らかのメモリの制限に達していることは確かなようですね。

    `new UploadHandlerRaw()`や`new DownloadHandlerBuffer()`は、`Dispose()`しないとメモリリークが生じる可能性があります。
    `using (~){}`の中で使う方法が安全ではないでしょうか。
  2. @fuzigiwa2

    Questioner

    using (~){}で処理を行うようにしてみましたが、変わらず同じエラーでハングしてしまいました。
    恐らく処理中にメモリリークしているので、解放されていないためのメモリリークではないようです。

    他に方法などご存じでしょうか。
  3. 残念ながら、扱っておられる課題については詳しく存じません。
    お力になれず申し訳ございません。

    書かれたコードに問題が無いのにエラーが解消できないのでしたら、症状を再現可能な最小のプロジェクトを作成して、レポートツール(Report a Problem with Unity)を用いて報告されることをお勧めします。
    やりとりに時間はかかりますが、問題の解決まで丁寧に対応していただけます。
  4. @fuzigiwa2

    Questioner

    こちらですが、ファイルを一度Byte配列にしておりますので、
    サーバーに分割して送ることなど出来るのでしょうか。

    メモリリークしないサイズ(50MB区切りなど)で送り続け、
    サーバーで結合して対応することが出来るのでしたら
    現状の使用しているクラスを変更することなく対応できるかと考えております。

    そのようなやり方などご存じでしょうか。

作成したコルーチン処理として編集いたしました。
l_file_dataは参照されているだけであってファイルのバイト数分のメモリ使用しかされていないと考えております。これはWindowsエディタ上のメモリをウォッチしたところ大きな増加がみられなかったこととXcode上で「exc_resource resource_type_memory (limit=1850 mb unused=0x0)」が発生したことからメモリリークと判断しております。

0Like

Your answer might help someone💌