C # / XNA Гигантская утечка памяти

это мой первый пост здесь. Я делаю игру в Visual Studio 2010 с использованием XNA, и я столкнулся с гигантской утечкой памяти. Моя игра начинается с 17КБ оперативной памяти, а затем через десять минут до 65К. Я запустил некоторые профилировщики памяти, и все они говорят, что создаются новые экземпляры объекта String, но они не являются живыми. Количество живых экземпляров String не изменилось вообще. Он также создает экземпляры Char [] (чего я и ожидал), Object [] и StringBuilder. Моя игра довольно новая, но здесь слишком много кода для публикации. Я понятия не имею, как избавиться от не живущих экземпляров, пожалуйста, помогите!

 John Smith07 июн. 2012 г., 05:48
Deleaker, Valgrind, VLD ...?
 Kirbyfanner07 июн. 2012 г., 02:39
Ладно, будем, если все в порядке, тогда я просто позволю этому упасть. Это сбрасывает время от времени. Спасибо! И да, я имел в виду 65000k лол
 Dan Bryant07 июн. 2012 г., 01:16
Я предполагаю, что вы должны иметь в виду до 65 000 КБ, так как я не думаю, что вы сможете даже сократить размер процесса до 17 КБ. Даже тогда 65M не так уж велик по современным стандартам.
 Andy West07 июн. 2012 г., 00:39
Я не уверен, что считаю 65k "гигантской утечкой памяти". Вы могли бы почти соответствовать этому на персональном компьютере 30 лет назад. Если бы он дул до 650 МБ, я бы немного волновался.

Ответы на вопрос(2)

чтобы дать вам более обоснованное предположение. Вот мое обоснованное предположение:

Если вы делаете такие вещи в своем методе Draw:

spriteBatch.DrawString(font, "Score: " + score, location, Color.Black);
spriteBatch.DrawString(font, "Something else: " + foo, overHere, Color.Black);
spriteBatch.DrawString(font, "And also: " + bar, overThere, Color.Black);

Затем каждый из этих звонков будет создавать новыеstring а такжеStringBuilder объекты за вашей спиной каждый раз, когда они бегут. Потому что они в вашемDraw метод, каждый, вероятно, работает 60 раз в секунду. То есть много временных объектов выделяется!

Чтобы убедиться, что это так - используйте CLR Profiler. Похоже, вы уже сделали это.

Пока это на самом деле не «утечка» - сборщик мусора в конечном итоге их очистит - такая схема распределения нежелательна в игре. Видеть это сообщение в блоге о двух методах борьбы со сборкой мусора в игре. Метод 1 обычно проще и дает лучшие результаты, поэтому я обсуждаю его здес

Стоит упомянуть, что сборщик мусора на ПК достаточно быстр, чтобы выделение было таким не имеет значения. GC очистит крошечные объекты (например, ваши временные строки) с минимальными накладными расходами.

На Xbox 360, с другой стороны, даже регулярное производство такого небольшого количества мусора может привести к серьезным проблемам с производительностью. (Я не уверен насчет WP7, но я бы лично отнесся к нему как к Xbox - с осторожностью!)

Как мы это исправим?

Ответ прост:DrawString примет экземплярStringBuilder на местеstring. Создайте один экземплярStringBuilder, а затем используйте его каждый раз, когда вам нужно собрать пользовательскую строку.

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

Вот тот, который я использую в форме метода расширения, для добавления целых чисел в строку без выделения:

public static class StringBuilderExtensions
{
    // 11 characters will fit -4294967296
    static char[] numberBuffer = new char[11];

    /// <summary>Append an integer without generating any garbage.</summary>
    public static StringBuilder AppendNumber(this StringBuilder sb, Int32 number)
    {
        bool negative = (number < 0);
        if(negative)
            number = -number;

        int i = numberBuffer.Length;
        do
        {
            numberBuffer[--i] = (char)('0' + (number % 10));
            number /= 10;
        }
        while(number > 0);

        if(negative)
            numberBuffer[--i] = '-';

        sb.Append(numberBuffer, i, numberBuffer.Length - i);

        return sb;
    }
}
 GGulati07 июн. 2012 г., 04:54
Тебе технически не нужен numberBuffer; просто добавляйте 1 символ за раз (быстрее из-за уменьшения попаданий в кеш). Если вас беспокоит изменение размера, используйте sb.EnsureCapacity (sb.Length + 16), чтобы предотвратить его (16, чтобы сохранить выравнивание строк кэша).
 Kirbyfanner07 июн. 2012 г., 16:47
Ого, в этом есть много смысла. Я попробую это, как только смогу, большое спасибо!
 Andrew Russell08 июн. 2012 г., 14:37
@ GGulati Пока ты прав, что звонишьAppendимволы @ on работают немного лучше, чем построение строки в буфере и ее размещение на месте, отметим, что алгоритм строит строку справа налево. Я оставлю выяснение алгоритма слева направо в качестве упражнения;) Более того: нет смысла беспокоиться оEnsureCapacity. Все дело здесь в том, чтобы повторно использовать буфер. Таким образом, повышение производительности произойдет один раз. И время процессора такая маленькая разница. Это специфическая оптимизация GC. Плохая форма для наложения лишних лишних оптимизаций сверху.

очен трудно достать). То, что вы испытываете, это нормально. Сборщик мусора не «чувствует», как будто ему нужно собирать память, поэтому он этого не делает. Всякий раз, когда память заканчивается, сбор мусора будет происходить. Если выабсолютн уверен, что вы не храните ненужные ссылки на вашstring s, тогда все хорошо.

Если вы хотите использовать цикл GCGC.Collect().

 Daniel Little07 июн. 2012 г., 01:32
С помощьюGC.Collect в игре не очень хорошая идея, лучше использовать пул объектов внутри игрового цикла и просто избегать динамического распределения.
 Steven Du06 янв. 2013 г., 17:02
Некоторая часть XNA не является управляемым кодом, поэтому GC не будет работать. так : / Stackoverflow.com вопросы / 3364481 / ...

Ваш ответ на вопрос