Моя команда работает над проектом 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);
}