Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.

Question Unity 2021 - UnityWebRequest - Native collection memory leak

Discussion in 'Scripting' started by stevenworks_unity, Sep 28, 2021.

  1. stevenworks_unity

    stevenworks_unity

    Joined:
    Jan 15, 2020
    Posts:
    24
    Hello,
    in Unity 2021, the UnityWebRequest calls sometimes rise a "Native collection... memory leak" Console message.

    My code is pretty simple:

    Code (CSharp):
    1. public static async UniTask<string> MyHttp(string url, string content)
    2.         {
    3.             var data = "";
    4.  
    5.             using (UnityWebRequest httpRequest = UnityWebRequest.Post(url, content))
    6.             {
    7.                 httpRequest.method = UnityWebRequest.kHttpVerbPOST;
    8.                 httpRequest.downloadHandler = new DownloadHandlerBuffer();
    9.                 httpRequest.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(content));
    10.  
    11.                 httpRequest.SetRequestHeader("Content-Type", "application/json");
    12.                 httpRequest.SetRequestHeader("Accept", "application/json");
    13.  
    14.                 await httpRequest.SendWebRequest();
    15.  
    16.                 data = httpRequest.downloadHandler.text;
    17.                    
    18.                 httpRequest.downloadHandler.Dispose();
    19.                 httpRequest.uploadHandler.Dispose();
    20.                 httpRequest.Dispose();
    21.  
    22.             }
    23.  
    24.             return data;
    25.  
    26.         }
    In the Console, the "memory leak" error comes from the "using" (#5) line of code.
    The curious thing is that the error appears randomly.

    Can someone help me resolve this?

    Cheers.
    Stefano
     
  2. stevenworks_unity

    stevenworks_unity

    Joined:
    Jan 15, 2020
    Posts:
    24
    Hi,
    trying to resolve this issue by myself, I notice something strange.

    From the Unity manual, it appears that when I use the UnityWebRequest.Post() method, I must specify the data to send. However, I can also use the .uploadHandler = new UploadHandlerRaw() code to define the data to send.

    Now, said that, I expected that the data parameter of the UnityWebRequest.Post() was optional and not mandatory, so I can choose if to send my data with the Post() method or with the UploadHandlerRaw() method.

    So, as an attempt, I modified this line of code:
    using (UnityWebRequest httpRequest = UnityWebRequest.Post(url, content))

    with this:
    using (UnityWebRequest httpRequest = UnityWebRequest.Post(url, string.empty))

    and now everything seems to work and the "Native Collection..." error doesn't appear!

    Any opinion about this?
     
    Last edited: Sep 28, 2021
    Zotooo likes this.
  3. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,671
    UnityWebRequest.Post assigns an upload handler for you.
    With the way you create web request, just do new UnityWebRequest() and pass relevant arguments to constructor.
    Also, you use the using block, so you don't need to Dispose() the request, using block does this automatically for you.
     
    Bunny83 likes this.
  4. stevenworks_unity

    stevenworks_unity

    Joined:
    Jan 15, 2020
    Posts:
    24
    Thanx for the info!

    However, in my case, if I use the postData parameter of the UnityWebRequest.Post() the PHP script I have in my Server doesn't receive anything.

    Instead, if I use the UploadHandlerRaw() method everything works fine.
     
    Last edited: Sep 28, 2021
  5. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,671
    The string parameter in Post() assumes HTML form and does perform escaping. If your string is not HTML form, then escaping might be corrupting the string and your PHP server doesn't accept it afterwards.
    So, like I said: use constructor.
     
  6. stevenworks_unity

    stevenworks_unity

    Joined:
    Jan 15, 2020
    Posts:
    24
    Hi,
    ok, I've understood and I'm using the constructor. Everything works ok.

    However, in my very personal opinion, I think that the online manual is not very clear on this. There are no clear examples of how to use the UnityWebRequest class. And the curious thing is that googling around I find a lot of people who are using this class in the wrong way!!! Maybe, on the UploadHandlerRaw() page, an example that shows how to send post data using the constructor would have been very useful.

    Anyway, thank you for your support! Really appreciated!
     
  7. ofirudwork

    ofirudwork

    Joined:
    Nov 3, 2021
    Posts:
    17
    Hi
    I'm getting this error too.
    Have you handled it?
     
  8. Michael_Swan

    Michael_Swan

    Joined:
    Aug 24, 2021
    Posts:
    49
    @stevenworks_unity
    Code (CSharp):
    1.  
    2.             string projRequest = sb.ToString(); // string is NOT HTML
    3.             string projectionUri = _useDevHeightTransformEndpoint ? TransformConstants.DevProjectionUri : TransformConstants.ProjectionUri;
    4. // Leave string.empty so that it doeesn't create a second uploadHandler that causes a memory leak
    5.             using (UnityWebRequest client = UnityWebRequest.Post(projectionUri, string.Empty /*projRequest*/))
    6.             {
    7.                 client.SetRequestHeader("Content-Type", "application/json");
    8.                 client.SetRequestHeader("Accept", "application/json");
    9.                 client.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(projRequest));
    10.                 client.uploadHandler.contentType = "application/json";
    11.              
    12.                 yield return client.SendWebRequest();
    13.  
    So, the above doesn't leak.

    but this does...
    Code (CSharp):
    1. using (UnityWebRequest client = UnityWebRequest.Post(projectionUri, projRequest))
    and as suggested I'm "using the constructor". I imagine that the projRequest get mangled above too - it's not html, it's json.

    How do I use the constructor to send Encoding.UTF8.GetBytes(projRequest) and not use new UploadHandler().
    Code (CSharp):
    1. using (UnityWebRequest client = UnityWebRequest.Post(projectionUri,  Encoding.UTF8.GetBytes(projRequest))
    doesn't work either.

    upload_2022-5-5_15-27-14.png

    Which is interesting because there is a Put byte[] function but no Post one.
    Code (CSharp):
    1. //
    2.         public static UnityWebRequest Put(string uri, byte[] bodyData);
    Note: I didn't write the code above - just trying to fix the memory leak properly.
    Looks like projRequest is trying to send json - which I imagine the old author is changing to byte stream to avoid the html mangling.
    upload_2022-5-5_15-35-47.png
    Cheers
     
  9. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,623
    Have you actually read any reply that was posted here? :)

    It seems you don't really understand what a constructor is.

    Just to be more explicit here. The Post method is not a constructor. When you use the static Post method to create a webrequest, Unity creates an upload and download handler for you. However this upload handler will expect the string to represent an URL encoded HTML form string. So something like
    field=42&other=foobar&another=123
    . Such a string would be URL encoded. So providing any other string to the Post method would most likely corrupt the data as it is URL encoded.

    So as it was mentioned several times, when you want to post raw data, do not use the static Post method. Instead just create a UnityWebRequest the usual way by using the constructor.

    So instead of using

    Code (CSharp):
    1. using (UnityWebRequest client = UnityWebRequest.Post(projectionUri, string.Empty /*projRequest*/))
    use
    Code (CSharp):
    1. using (UnityWebRequest client = new UnityWebRequest(projectionUri, "POST"))
     
    efge likes this.
  10. Michael_Swan

    Michael_Swan

    Joined:
    Aug 24, 2021
    Posts:
    49
    Hi,

    Thanks for the reply. I changed the code to the following -

    Code (CSharp):
    1.  string projRequest = sb.ToString();
    2.             string projectionUri = _useDevHeightTransformEndpoint ? TransformConstants.DevProjectionUri : TransformConstants.ProjectionUri;
    3.  
    4.             UploadHandler uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(projRequest));
    5.             DownloadHandler downloadHandler = new DownloadHandlerBuffer();
    6.             using (UnityWebRequest client = new UnityWebRequest(projectionUri, UnityWebRequest.kHttpVerbPOST, downloadHandler, uploadHandler)) // projRequest))
    7.             {
    8.                 client.SetRequestHeader("Content-Type", "application/json");
    9.                 client.SetRequestHeader("Accept", "application/json");
    10.                 client.uploadHandler.contentType = "application/json";
    11.  
    12.                 yield return client.SendWebRequest();
    13.  
    14.                 bool hasError = HttpUtils.DidConnectionSucceed(client);
    15.                 if (hasError)
    16.                 {
    17.                     Debug.Log("Proj Service encountered Error: " + client.error + " - " + client.responseCode.ToString());
    18.                 }
    19.                 else if(client.isDone)
    20.                 {
    21.                     string projResponse = client.downloadHandler.text;
    22.  
    23.                     Debug.Log("Proj Service gave Response: " + projResponse);
    etc.

    This bug is fairly nasty -
    I had to follow this:
    https://forum.unity.com/threads/sol...n-disposed-resulting-in-a-memory-leak.548929/
    Just to get a stack trace, and I'll admit I did speed read this, and I did miss it - thank-you for your help.
     
    Bunny83 likes this.
  11. munna_unity

    munna_unity

    Joined:
    Oct 10, 2022
    Posts:
    1
    Code (CSharp):
    1. public void WatchVideo()
    2.     {
    3.         OpenModal.Open(false);
    4.         StartCoroutine("SaveAdsRewardToServer");
    5.     }
    6.  
    7.     IEnumerator SaveAdsRewardToServer()
    8.     {
    9.         PlayerInfo.instance.OpenLoading(true, "Please wait checking wallet balance");
    10.  
    11.         string url = StaticStrings.ludoApi + "claim/bonus/rewards";
    12.         WWWForm wwwForm = new WWWForm();
    13.         wwwForm.AddField("reward", reward.ToString());
    14.  
    15.         using (UnityWebRequest webRequest = UnityWebRequest.Post(url, wwwForm))
    16.         {
    17.             //UnityWebRequest webRequest = UnityWebRequest.Post(url, wwwForm);
    18.             webRequest.SetRequestHeader("Authorization", "Bearer " + GameManager.Instance.accessToken);
    19.  
    20.             // make dispose
    21.             webRequest.disposeUploadHandlerOnDispose = true;
    22.             webRequest.disposeDownloadHandlerOnDispose = true;
    23.  
    24.             // make web request
    25.             yield return webRequest.SendWebRequest();
    26.  
    27.             // Hide loading
    28.             PlayerInfo.instance.OpenLoading(false);
    29.  
    30.             if (webRequest.result != UnityWebRequest.Result.Success)
    31.             {
    32.                 string text = webRequest.downloadHandler.text;
    33.                 Response response = JsonConvert.DeserializeObject<Response>(text);
    34.                 PlayerInfo.instance.OpenErrorBox(true, response.message);
    35.                 webRequest.Dispose();
    36.             }
    37.             else
    38.             {
    39.                 string text = webRequest.downloadHandler.text;
    40.                 UserModel userModel = JsonConvert.DeserializeObject<UserModel>(text);
    41.                 GameManager.Instance.userModel = userModel;
    42.                 PlayerInfo.instance.CoinSound();
    43.                 PlayerInfo.instance.OpenErrorBox(true, userModel.message, "Reward Granted!");
    44.                 PlayerInfo.instance.SetProfileInfo();
    45.                 webRequest.Dispose();
    46.                 //OpenModal.Open(false);
    47.             }
    48.         }
    49.     }
    In my case
    When i close popup modal then root class was disabled after start coroutine, then i closed this popup model when finish the request and solved
     
  12. Zotooo

    Zotooo

    Joined:
    Aug 14, 2022
    Posts:
    2


    Thank you very much this really solved my problem