How to spot memory leaks,Memory profiler show multiple empty names with same dimension

Hello, I have an application that runs on android wall touchscreens that is intended to run forever, I’ve spent lot of time trying to optimize it (check my other posts if u want) and I’m now investigating on memory leaks.

I’m using the Unity Memory Profiler directly on the android device. I can see under the “not saved” section, alot of textures2d without name that progressively multiply but I’m struggling to find a correlation to my asset or my scripts.

In this screenshot you see the biggest one (23,4mb) but there are alot of other of smaller dimension. The more the app is used the more those “noname” pops. Is there a way to identify them?

I tried:

  • call Resources.UnloadUnusedAssets() every minute. This does indeed do reduce memory usage, but not for everything
  • check everytime i use “new Texture” in my code and give those objects a name. I can see my textures named correctly, but not those with noname.
  • check other assets form the profiler to find the ones with the same dimension. My suspect is that the big 23,4 mb texture is the assetbunle with all my icons that is created runtime (under “scene memory” i see only one noname texture with the same dimension that has 252 ref count, showing references basically all around my hierarchy)

I really don’t know how to find the issue here. Thanks

Hello,
Do these textures, or some at least some of these, still have references to them, i.e. a blue link text number >0 in the References column of the table? If so, click on those and follow the breadcrumbs. That might help you find out what’s holding them in memory and maybe even brought them there without a name.

None of the big ones, but yes some of those noname has a ref count > 0, but, how I read those informations?

That’s the managed wrapper Texture2D object that “References” the native Texture2D object. This also means there is some C# code holding onto this wrapper, which is indicated by the blue 1 in the References Column.

We’re working towards a better UI solution but right now, you kinda have to click through those Reference count numbers to get towards what is actually holding these.

Unfortunately the Memory profiler doesn’t help me to find my issue, I’ve found a bit easier to use the “normal” profiler with the “take sample” function. It seems to give me better informations. At the moment I’m profiling my app, taking samples after each iteraction on the app and checking the diffs by hand.

Thanks to this i’ve found a strange behaviour: I have a script that show a webcam snapshot into a RawImage and refresh it every 5 seconds, each time a new image arrives I destroy the old texture, i nullify it and i put the new texture into the rawImage. everything is fine, but I’ve found that a noname texture will be sent into the memory (and it stays there till the app is closed) when the object that holds that image is destroyed.

my OnDestroy method was like this:

        private void OnDestroy()
        {
            Destroy(texture);
            texture = null;
        }

I thought it was enough, and the texture will be destroyed by unity itself but it seems not enough, as soon as i add this

            if (view.texture != null) Destroy(view.texture);
            view.texture = null;

(view is the RawImage component)
no more noname texture appears.

… an update: …That’s not true all the time. Now the empty textures are reduced. but they still appears. I can see the destroy method called, but still around 50% of the time, one texture remains in the memory

I have probably found the issue. the method that download the image is this:

 IEnumerator IJpegStream()
        {
            if (!Uri.IsWellFormedUriString(URI, UriKind.RelativeOrAbsolute))
            {
                OnCameraError("URI Malformed");
                yield break;
            }
           
                using (uwebRequest = UnityWebRequestTexture.GetTexture(URI, false))
                {
                    uwebRequest.disposeDownloadHandlerOnDispose = true;
                    if (AuthenticationType != HttpAuthenticationType.NONE)
                    {
                        uwebRequest.SetRequestHeader("AUTHORIZATION", Config.EncodedCredentials);
                    }

                    yield return uwebRequest.SendWebRequest();

                    if (!uwebRequest.isNetworkError && !uwebRequest.isHttpError)
                    {
                        if (uwebRequest.isDone)
                        {
                         
                      
                            texture = DownloadHandlerTexture.GetContent(uwebRequest);
                            texture.name = "JpegVideoStream_DownloadHandler";
                       
                             
                                Destroy(view.texture);
                                view.texture = null;
                                view.texture = texture;
                               
                                OnFrameReceived();
                           
                        ,,,

seems that, if I destroy the object before the download, UnityWebRequestTexture is still running and the downloaded texture will sit in the memory. Now, I tried to abort the request with:

if (uwebRequest != null && !uwebRequest.isDone)
            {
                uwebRequest.Dispose();   
            }

But unity throws this error:
ArgumentNullException: Value cannot be null.
Parameter name: _unity_self

But uwebrequest is indeed not null when i debug it… what’s wrong now? :frowning:

The closing curly bracket of your using(){} scope already compiles to the same as calling uwebrequest.Dispose(). The using scope just does it automatically, even if an exception or early return call in the scope would abort the normal execution flow of the code in the scope. So now your calling Dispose twice which might cause this error? To abort the request, you should call Abort() on it I think. But I’m not sure I 100% understand the flow and where you’re trying to abort it, or why.

I’m just trying unusual stuff at this point as I clearly see new textures without name being created in the memory (and never destroyed) if i destroy the object that held that script before the download is completed.

I know about the using behaviour but seems it is not disposed neither with stopping the coroutine neither detroying the object. I think that the UnityWebRequest task is launched but if unexpectedly closed it wont be disposed by unity… (or at least it seems so)

About the error, that piece of code is inside the OnDestroy() method. I first tried with Abort, but I had the same error.

Are you stopping the coroutine in OnDestroy? That should handle the Disposing but might still need an Abort on the handle and a Destroy on the texture in the view?

I think I’ve spotted an unity bug here. It is also easy reproducible: I created a simple script that simple start a coroutine that download a texture from web with unitywebrequestTexture, and another script that keep adding and removing that script in a gameobject. What I notice is this:

with ondestroy() empty:

  • the texture downloaded from web is spammed inside the “not saved textures” from the profiler.

with ondestroy() actually destroying the texture downloaded

  • same result

with ondestroy() containing this:

try
            {
                if (uwebRequest != null && !uwebRequest.isDone)
                {          
                    uwebRequest.Abort();
                    Debug.Log("success aborting");
                }
            }
            catch (Exception e)
            {
                Debug.Log("Failed " + e);
            }
  • on a loop of 1000 textures, 4 of them leaked into memory. I can see 893 Success and 103 Failed. Means that in a controlled loop 4 times the ondestroy has been called with the unitywebrequest that has done his job, BUT still leaked out the downloaded (and destroyed) texture.

@MartinTilo Sorry I’m still here, did you had a chance to try what I noticed? Just to know if is a real issue or if is just something that I make it wrong

Hi,
No I didn’t have a chance to test this yet and am headed into a long weekend. It might be worth a bug report. If you filed one, I could then also grab the repro project from there if you post the issue ID number here. I’m still somewhat expecting this to be a timing issue between the co-routine and the OnDestroy though. I just can’t be sure from the snippets and without finding the time to try it myself…