Страница 1 из 2

После удаления объектов память не освобождается

СообщениеДобавлено: 26 апр 2020, 18:21
Инженер
Я периодически очищаю пул от лишних объектов. Мне казалось это хорошей идеей, пока не посмотрел в диспетчер задач при тестировании сборки. Объекты удаляются, но память не снижается! То есть когда пул мотает объекты туда-сюда, память не растет. Но стоит инстанцировать несколько тысяч новых объектов, как память вырастает. И так и остается использованной на всегда, даже после удаления объектов полностью (я проверял)!

Что за дела? Я хочу узнать причину, почему это происходит и как очистить память полностью, не перезагружая сцену.

Я думал, может пул поломанный. Для чистоты теста построил отдельный чистый проект с простым скриптом. Никаких других ассетов, текстур и т.д. не используется, только чистые дефолтные кубы, упакованные в два префаба. Один спаунится обычно, по ссылке на GameObject в скрипте, второй с помощью Resources.Load. Я думал, может будет разница, но разницы нет.

Итого чистую сцену могу довести до 1.5 Гб занятой памяти, хотя ни единого объекта в сцене нет. Нужно предварительно наспаунить несколько десятков тысяч объектов, а потом уничтожить их и повторить несколько раз.

Синтаксис:
Используется csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Test
{
    public class PrefomanceMemorySpawnDestroyObjects : MonoBehaviour
    {
        int cycles = 1000;
        public GameObject prefab;
        List<GameObject> list = new List<GameObject>();

        private void Update()
        {
            if (Input.GetKey(KeyCode.Alpha1))
            {
                SpawnPrefab();
            }

            if (Input.GetKey(KeyCode.Alpha2))
            {
                SpawnResource();
            }

            if (Input.GetKeyDown(KeyCode.Alpha3))
            {
                DestroyAll();
            }

            if (Input.GetKeyDown(KeyCode.Alpha4))
            {
                ClearMemory();
            }
        }


        void SpawnPrefab()
        {
            for(int i = 0; i < cycles; i++)
            {
                GameObject go = Instantiate(prefab);
                Vector3 pos = new Vector3(Random.Range(-100f, 100f), Random.Range(-100f, 100f), Random.Range(-100f, 100f));
                go.transform.position = pos;
                list.Add(go);
            }
        }

        void SpawnResource()
        {
            for (int i = 0; i < cycles; i++)
            {
                GameObject go = GameObject.Instantiate(Resources.Load("1", typeof(GameObject))) as GameObject;
                Vector3 pos = new Vector3(Random.Range(-100f, 100f), Random.Range(-100f, 100f), Random.Range(-100f, 100f));
                go.transform.position = pos;
                list.Add(go);
            }
        }

        void DestroyAll()
        {
            foreach(GameObject go in list)
            {
                Destroy(go);
            }
            list = new List<GameObject>();
        }

        void ClearMemory()
        {
            Resources.UnloadUnusedAssets();
            System.GC.Collect();
        }

    }
}
 


Если хотите протестировать, вот unitypackage: https://www.dropbox.com/s/agw1iyxuv3iv738/MemTest.unitypackage?dl=0

Можете еще сборку потестить, в диспетчере задач все в реальном эксперименте можно увидеть. Создайте столько объектов, пока сборка не займет 2 ГБ памяти, а потом нажмите 3- увидите, что теперь где-то 1 гб занимает сборка, хотя изначально было в районе 70-100 мб.
(цифры 1 или 2- спаун, 3- удаление, 4- оптимизация)- https://www.dropbox.com/s/f3w1erpd4zv0nsf/MemoryTest.rar?dl=0

Re: После удаления объектов память не освобождается

СообщениеДобавлено: 26 апр 2020, 18:52
1max1
Меня давно тоже интересовала эта тема, я даже создал топик на офф форуме, но все похер, видимо их устраивает, то что юнька жрет непомерно и не очищает нифига. Короче, я смирился и тебе советую, хотя, можешь попробовать https://habr.com/ru/post/496460/
Потом расскажешь, как оно)

Re: После удаления объектов память не освобождается

СообщениеДобавлено: 26 апр 2020, 19:43
Woolf
И тут люди узнали, как работает и что такое GarbageCollector..

Re: После удаления объектов память не освобождается

СообщениеДобавлено: 26 апр 2020, 21:19
Jarico
Чувак! Ты создал объекты, для каждого объекта в нативном пуле создалось место под объект и ресурсам которые он использует, чтобы выгрузить полностью память тебе нужно удалять сам объект и ресурсы которые он испольует...

При загрузке с помощью Resources.Load ты должен каждый ресурс который нужен объекту загружать и назначать, иначе юнити сама загружает ресурсы для объекта но после удаления объекта ссылки на ресурсы теряются и получается так что они валяются в памяти до вызова очистки памяти (очистка происходит при смене сцены)

Re: После удаления объектов память не освобождается

СообщениеДобавлено: 26 апр 2020, 23:21
Инженер
Woolf писал(а):И тут люди узнали, как работает и что такое GarbageCollector..


В коде, как прекрасно видно, я вызываю сборщик мусора. Он не помогает.

Синтаксис:
Используется csharp
        void ClearMemory()
        {
            Resources.UnloadUnusedAssets();
            System.GC.Collect();
        }
 


1max1 писал(а):https://habr.com/ru/post/496460/
Потом расскажешь, как оно)

Пробовал, показалось слишком замороченным. Но если это единственный способ правильно расходовать память, придется снова присмотреться.

Jarico писал(а):Чувак! Ты создал объекты, для каждого объекта в нативном пуле создалось место под объект и ресурсам которые он использует, чтобы выгрузить полностью память тебе нужно удалять сам объект и ресурсы которые он испольует...


Как-то этот пункт не очень продуман у юнитеков. В примере я спауню кубы без текстур, какие там могут быть ресурсы? Нативные компоненты, что ли? И потом при уничтожении объекта для каждого компонента вызывать Destoy() или UnloadAsset?

Вообще, я думал Resources.UnloadUnusedAssets должен позаботиться об уничтожении не используемых ресурсов.

Re: После удаления объектов память не освобождается

СообщениеДобавлено: 26 апр 2020, 23:37
Jarico
Инженер писал(а):Как-то этот пункт не очень продуман у юнитеков. В примере я спауню кубы без текстур, какие там могут быть ресурсы? Нативные компоненты, что ли? И потом при уничтожении объекта для каждого компонента вызывать Destoy() или UnloadAsset?

Вообще, я думал Resources.UnloadUnusedAssets должен позаботиться об уничтожении не используемых ресурсов.


Я про это узнал когда решил весь контент в папку Resources запихнуть... Запихнуть запихнул а вот потом стал замечать что оперативная забивается и приходилось вообще юнити перезапускать...

Вообще класс Resources подразумевает полный контроль над ресурсами, т.е. для загрузки конкретного объекта...
А для твоих целей подойдут бандлы или Addressables (очень кста удобная фигня с самовыгрузкой контента)

Re: После удаления объектов память не освобождается

СообщениеДобавлено: 27 апр 2020, 12:04
Инженер
Jarico писал(а):Я про это узнал когда решил весь контент в папку Resources запихнуть... Запихнуть запихнул а вот потом стал замечать что оперативная забивается и приходилось вообще юнити перезапускать...


Так, стоп, я кое-что упустил. Ведь у меня в скрипте спаунятся и обычные префабы НЕ из папки ресурсов, а по ссылке на префаб, находящемся в Assets:

public GameObject prefab;

Я предусмотрел, что способ инстанцирования может влиять на память, поэтому решил заодно проверить оба варианта. Память растет точно так же. То есть вообще нет разницы, спаунить из ресурсов с помощью

GameObject.Instantiate(Resources.Load("1", typeof(GameObject))) as GameObject;

или обычным способом:

Instantiate(prefab);

Чем же Resources хуже обычного инстанцирования префабов? Судя по моему тесту, они абсолютно одинаково пожирают память и конкретно пристрастие к Resources тут ни при чем.

Re: После удаления объектов память не освобождается

СообщениеДобавлено: 27 апр 2020, 20:49
seaman
Unity’s garbage collection – which uses the Boehm GC algorithm – is non-generational and non-compacting. “Non-compacting” means that objects in memory are not relocated in order to close gaps between objects.

https://docs.unity3d.com/Manual/BestPra ... ty4-1.html

Т.е. после удаления из кучи объектов образовавшиеся в куче "дырки" не убираются. Отсюда куча не уменьшается. Отсюда память растет.
Дырки используются для размещения новых объектов. Но если объект в дырку не влазит, то куча увеличивается.

В-общем занимаемая память может только увеличиваться...

Re: После удаления объектов память не освобождается

СообщениеДобавлено: 27 апр 2020, 21:14
1max1
В-общем занимаемая память может только увеличиваться...

Кайф... :ymparty:

Re: После удаления объектов память не освобождается

СообщениеДобавлено: 27 апр 2020, 21:18
Alex5
Так, а шо ви таки хочите? :) Это же обычная куча, как в том же си. Хотите кучу без дырок - стройте менеджер. И вручную, полегоньку, тасуйте объекты

Re: После удаления объектов память не освобождается

СообщениеДобавлено: 28 апр 2020, 17:01
Инженер
seaman писал(а):
Unity’s garbage collection – which uses the Boehm GC algorithm – is non-generational and non-compacting. “Non-compacting” means that objects in memory are not relocated in order to close gaps between objects.

https://docs.unity3d.com/Manual/BestPra ... ty4-1.html

Т.е. после удаления из кучи объектов образовавшиеся в куче "дырки" не убираются. Отсюда куча не уменьшается. Отсюда память растет.
Дырки используются для размещения новых объектов. Но если объект в дырку не влазит, то куча увеличивается.

В-общем занимаемая память может только увеличиваться...


Теперь все ясно, спасибо за ссылку.

Интересно, а Addressable Assets тоже имеет эту проблему? Там упоминается скромное "The address not only loads assets, but also unloads them. References are counted automatically and a robust profiler helps you spot potential memory problems.", но решает ли это проблему расширяющейся кучи? Руководство очень куцое.

Alex5 писал(а):Хотите кучу без дырок - стройте менеджер. И вручную, полегоньку, тасуйте объекты


Это как? Загружать каждый компонент объекта отдельно, а потом их уничтожать отдельно? Что имеется ввиду?

Re: После удаления объектов память не освобождается

СообщениеДобавлено: 28 апр 2020, 19:18
Alex5
Инженер писал(а):Это как? Загружать каждый компонент объекта отдельно, а потом их уничтожать отдельно? Что имеется ввиду?

Не заморачивайтесь. Имеется ввиду то, что можно создать низкоуровневый менеджер памяти, который не будет жрать память. Да, это сложно, новичку не потянуть точно, да и выгода от него будет весьма эфемерна.
Проще изначально просчитать будущую архитектуру на бумаге, отложить в сторону, а через неделю сократить на 10-30%.

Re: После удаления объектов память не освобождается

СообщениеДобавлено: 10 май 2020, 13:22
Инженер
Значит, вариантов уменьшить кучу без перезагрузки сцены нет? А как в других движках дела обстоят? В Unreal Engine, Godot? Описанное ниже и в них тоже происходит?
"после удаления из кучи объектов образовавшиеся в куче "дырки" не убираются. Отсюда куча не уменьшается. Отсюда память растет. Дырки используются для размещения новых объектов. Но если объект в дырку не влазит, то куча увеличивается."

Re: После удаления объектов память не освобождается

СообщениеДобавлено: 10 май 2020, 14:31
DbIMok
не надо путать Unity память и Mono память. первая это про загрузку/выгрузку ассетов, вторая про то что может почистить GC. вы смотрели в Profiler что происходит?

Re: После удаления объектов память не освобождается

СообщениеДобавлено: 11 май 2020, 22:37
Инженер
В профайлере увеличивается Unity-память. Да что тут разбираться, все уже и так понятно объяснили знающие люди- после удаления объектов в куче образуются дырки, в дырки не влезают новые данные, куча расширяется, чтобы влезло... Единственное, что я хотел узнать- можно ли что-то с этим поделать без перезагрузки сцен.

Сейчас тыкаю палочкой Unreal Engine 4, главная цель- повторить такой же тест в этом движке и посмотреть, будет ли память так же бесконечно увеличиваться. Пока затык с местными типами в С++, потому что там не Queue, а какой-то TQueue и почему-то не получается записать туда Actor (местный GameObject), хотя делаю вроде все правильно. Если есть чем помочь, прошу посмотреть код: https://forums.unrealengine.com/development-discussion/c-gameplay-programming/1758991-why-can%E2%80%99t-i-announce-the-queue

На крайний случай, если в Unreal все так же плохо, есть еще Unigine. Недавно выпустили бесплатную версию, графоуний впечатляет. Главное, что он заточен под открытые пространства и стриминг объектов, может хоть там этот вопрос с кучей решен. Минус, что вводных уроков нет, кроме официальной документации. Я его немного помучил, столкнулся с отсутствием автозаполнения в Visual Code (баг какой-то) и решил отложить до лучших времен.

Ну и Godot еще есть, тоже можно будет ему проверку памяти устроить. Скоро разрабы поддержку Vulkan добавят, графоний до небес взлетит.

Обнадеживающее знамение: по запросу "heap expansion" находятся запросы только для Unity, для других движков упоминаний об этом свойстве я не нашел. Может быть, из-за меньшей популярности других движков, а может, у них такого минуса нет.