Search Unity

UnityWebRequest leak memory after upload zip

Discussion in 'Editor & General Support' started by sondtt, Sep 19, 2017.

  1. sondtt

    sondtt

    Joined:
    Dec 21, 2016
    Posts:
    4
    Hi everyone,
    I recently used UnityWebRequest and WWWForm to upload a zip file to server from mobile device (the server is written by myself). Everything worked fine except after upload the file, memory was not released and still remain a little bit.
    After severals time upload files, the app crash due to out of memory.
    The weird thing is UnityWebRequest seems cache the data in WWWForm and it cleanup when create another WWWForm. Here is the flow to reproduce:
    1. Before upload, used memory is 87.6mb
    2. Start uploading, memory is increase to 245mb
    3. Upload done, memory is release a little bit, used memory is 196mb
    4. Then upload another file, before uploading memory is about 200mb
    5. Uploading file, memory increase to around 280mb
    6. Upload done, used memory is 224mb

    Testing devices:
    iPhone6 + iPhone6 Plus (iOS 10.3.3)

    I have tried to fix this by:
    1. Dispose the UnityWebRequest after upload file
    2. Upgrade Unity from 5.5.3f1 to Unity 2017.1.1p.
    3. Replace UnityWebRequest with WWW class.

    All of the above attempt did not work!
    Did anyone meet this problem? Please show me how to fix it, I really appreciate :)
     
    Wakeel_Ahmed likes this.
  2. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    We would have to see the code. My guess would be that either you're holding onto a reference to WWWForm (or something that uses it), or you've got your zip file data in a stream that you haven't properly wrapped in a using block or disposed of so the resources are not being released.
     
  3. sondtt

    sondtt

    Joined:
    Dec 21, 2016
    Posts:
    4
    Hi Dustin,

    This is the code create WWWForm class with data
    Code (CSharp):
    1.  
    2. var zipForm = new WWWForm();
    3. zipForm.AddField("spot_id", m_PublishDataHandler.spot_id);
    4. zipForm.AddField("zip_file_count", m_PublishDataHandler.zipFiles.Count);
    5. zipForm.AddBinaryData("zip_file_" + localIndex, File.ReadAllBytes(m_PublishDataHandler.root_path + "_" + localIndex + ".zip"), m_PublishDataHandler.spot_id + "_" + localIndex + ".zip", "application/zip");
    6.  
    7. yield return ServerMgr.getInstance().upload(APIConfig.API_SPOT_UPDATE, zipForm
    8.     , (serverData) =>
    9.     {
    10.         isFailure = serverData.resultRequest != ServerResultRequest.R_SUCCESS;
    11.     }
    12.     , (uploadprogress, downloadprogress) =>
    13.     {
    14.         Debug.Log("upload zip " + localIndex + "'s progress: "+ uploadprogress + "-" + downloadprogress);
    15.     });
    16.  
    17. if (isFailure)
    18. {
    19.     cancelUpdateData();
    20.     yield break;
    21. }
    22.  
    And this is the body of uploading method (which use UnityWebRequest to upload file)
    Code (CSharp):
    1.  
    2. public IEnumerator upload(string url, WWWForm data, serverCallBack callback, UnityAction<float,float> processCallBack)
    3.         {            
    4.             m_unityWebRequest = UnityWebRequest.Post(url,data);
    5.             m_unityWebRequest.disposeDownloadHandlerOnDispose = true;
    6.             m_unityWebRequest.disposeUploadHandlerOnDispose = true;
    7.  
    8.             foreach (var header in mDefaultHeader)
    9.             {
    10.                 m_unityWebRequest.SetRequestHeader (header.Key, header.Value);
    11.             }
    12.             m_unityWebRequest.Send ();
    13.  
    14.             m_isStopUpload = false;
    15.             while (!m_isStopUpload)
    16.             {
    17.                 if (!m_unityWebRequest.isDone)
    18.                 {
    19.                     processCallBack(m_unityWebRequest.uploadProgress, m_unityWebRequest.downloadProgress);                  
    20.                     yield return null;
    21.                 }
    22.                 else
    23.                 {
    24.                     responseWebRequestCallBack(m_unityWebRequest, callback);
    25.                     m_isStopUpload = true;
    26.                     m_unityWebRequest.Dispose();
    27.                 }                
    28.             }
    29.         }
    30.  
     
  4. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,732
    How quick in succession to you do those requests? WWWForm will be destroyed by garbage collector, when there are no references to it. One thing to be aware of is that your coroutine must comple in order to lose all references to WWWForm object and all those callbacks you use, as those can keep things referenced.
    Also, it is not guaranteed that WWWForm will be destroyed immediately after completion, you need to do GC.Collect() for this to happen, and even then there is no guarantee it will be collected on first call and be aware that it is expensive and may stall you application. There is no better way now than to monitor your memory usage.
     
    Dustin-Horne likes this.
  5. sondtt

    sondtt

    Joined:
    Dec 21, 2016
    Posts:
    4
    Hi Arurimas,
    Thanks for your reply. I have some questions:

    Not too quick I think.
    I have tested the first time, then waiting for the memory to be released, but the memory still remain the same. I do not know exactly about the time, but 1 or 2 minutes is enough for the GC collect the unmanaged resouces, is it right? By the way, I also call GC.Collect() manually right after the upload progress but seems not worked.


    Can you tell me what is the right way to complete the coroutine? Does it complete when yield break called or run till the end of the function?
     
  6. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,732
    GC.Collect() does destroy managed resources. But that sounds like enough time.

    Both would end it, but if you do GC.Collect() from inside a coroutine, then it's not yet ended. For manual collection I'd start a new coroutine at the end of downloading coroutine and wait for at least a couple of frames in it before calling GC.Collect().
     
  7. sondtt

    sondtt

    Joined:
    Dec 21, 2016
    Posts:
    4
    Let me try call GC.Collect() after couple frames with new coroutine. Thanks in advance.
     
  8. unity_EDdq8x7gw4afZg

    unity_EDdq8x7gw4afZg

    Joined:
    Apr 30, 2019
    Posts:
    4
    Did you fix the problem? I am having the similar problem when upload video with WWWForm.
    I tried call GC.Collect() but not working. Can you share the solution?