(Как) можно форсировать сборку мусора и ждать завершения финализаторов на Unity 2019? - PullRequest
1 голос
/ 29 июня 2019

Моя команда работает над проектом Unity на C #. В некоторых местах мы называем System.GC.Collect() и System.GC.WaitForPendingFinalizers() (только в тестах, а не в рабочем коде. Почему мы это делаем, это не имеет отношения к моему вопросу, но объясняется ниже на случай, если кто-то заинтересован). Раньше это прекрасно работало на Unity 2018.1, но, похоже, перестало работать согласованно (то есть иногда кажется, что работает, но не всегда) с тех пор, как мы обновились до Unity 2019.1. Мы используем Mono на macOS и попробовали включить и выключить недавно добавленную функцию инкрементального ГХ, без заметной разницы.

Кто-нибудь знает что-нибудь, что изменилось между этими версиями Unity, что объясняет это? А кто-нибудь знает какой-то другой способ принудительного сбора мусора и ожидания ожидающих финализаторов, который работает на Unity 2019?

Вещи, которые я пытался безрезультатно: звонить GC.Collect() и GC.WaitForPendingFinalizers() несколько раз, спать в течение долгого времени после их вызова, выделять большие объемы памяти (не достаточно большой, я думаю, но опять же я тоже не не хочу толкать его).

Примечание: мы вызываем Collect() и WaitForPendingFinalizers() подряд; Я не знаю, какой из двух - тот, который на самом деле не выполняет ожидаемого (или того и другого), потому что только оба вместе имеют наблюдаемый эффект, который мы проверяем. Будут также оценены идеи, которые помогут сузить проблему.

Зачем нам это нужно

У нас есть класс, экземпляры которого, в зависимости от обстоятельств, могут быть долгоживущими, и в какой-то момент в их жизненном цикле хранятся большие объемы данных, которые впоследствии им больше не нужны. Чтобы освободить память, когда данные больше не нужны, поле, на которое они ссылаются, присваивается null.

У меня нет оснований полагать, что это не работает должным образом, но проблема в модульном тесте для этой функции, который раньше просто отлично работал на Unity 2018.1, но периодически терпел неудачу с тех пор, как мы перенесли наш проект в Unity 2019.1.

Пример кода

Ниже приведен (очень упрощенный!) Пример, который демонстрирует проблему: тест последовательно проходит на Unity 2018.1, но не на Unity 2019.1. Я старался сделать его как можно более минимальным. Я не знаю почему, но я не мог заставить его работать без использования List (аналогичная версия с одним object вместо List<object>, принятым последовательно также в Unity 2019.1) - это может или не может быть частью вопроса.

using System;
using System.Collections.Generic;

using NUnit.Framework;

public class MyClass {
    List<object> someData = new List<object>();

    public void RegisterData(object data) => someData.Add(data);

    public void ForgetData() => someData = null;
}

[TestFixture]
public class MyClassTest {
    class ObservablyFinalizable {
        readonly Action onFinalize;

        public ObservablyFinalizable(Action onFinalize) {
            this.onFinalize = onFinalize;
        }

        ~ObservablyFinalizable() {
            onFinalize();
        }
    }

    MyClass instance;
    bool isFinalized;

    [SetUp]
    public void SetUp() {
        instance = new MyClass();
        isFinalized = false;
    }

    [Test]
    public void ShouldForgetData() {
        WhenRegisteringObservablyFinalizableData();
        WhenForgettingData();
        WhenGarbageCollected();

        ThenFinalized();
    }

    void WhenRegisteringObservablyFinalizableData() =>
        instance.RegisterData(new ObservablyFinalizable(() => isFinalized = true));

    void WhenForgettingData() => instance.ForgetData();

    static void WhenGarbageCollected() {
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }

    void ThenFinalized() => Assert.That(isFinalized);
}
...