C# Selenium ChromeDriver Утечка памяти

Мне нужна помощь, поскольку я заметил, что селен chromeDriver в С# не освобождает память должным образом. Я использую RamMap для отслеживания использования памяти. Если не выполнять следующие сценарии, использование памяти не увеличивается постоянно.

Запуск приведенных ниже сценариев в планировщике задач приводит к увеличению памяти в записи RamMap «Unused-Active» при каждом запуске. Я подтвердил, что в мониторе ресурсов, диспетчере задач или RamMap нет активных chromedriver или процессов chrome. Selenium.WebDriver v4.23.0. Chrome и ChromeDriver v127.

Скриншоты RamMap для справки: видно, что запись «Unused-Active» значительно увеличивается примерно через 5 минут запуска сценариев. Это значение не увеличивается, когда сценарии не выполняются.

Основная проблема, как показано на скриншотах, заключается в том, что «Неиспользуемый — Активный» увеличивается с 7000 КБ до 50 000 КБ за 5 минут. В течение 24 часов, если так будет продолжаться, это увеличение составит почти 1 238 4000 КБ или около 12 ГБ. Это вызывает серьезную проблему с производительностью, поскольку после недели работы машина использует более 80% оперативной памяти, которая просто не используется, но все еще считается «активной».

До запуска скриптов (После перезагрузки) - Значение не увеличивается:

10 минут простоя - скрипты не выполняются

15 запланированных задач, запускающих консольное приложение каждую минуту в течение 5 минут. Ценность значительно возросла.

10 минут простоя - остановить запуск скриптов

Воспроизводимый код, который я пробовал: (Создайте консольное приложение C# и настройте проект для запуска 15 запланированных задач в планировщике задач каждую минуту.)

using OpenQA.Selenium.Chrome;
namespace ChromeBotTest
{
    public class Program
    {
        private static async Task Main(string[] args)
        {
            using (ChromeDriver? chromeDriver = new()) //using statement for automatic disposal
            {
                await Task.Delay(3000); //allow chrome to open for some short time
                chromeDriver.Close(); //close all windows
                chromeDriver.Quit(); //quit to fully close browser and release resources
            }
        }
    }
}
using OpenQA.Selenium.Chrome;
namespace ChromeBotTest
{
    public class Program
    {
        private static async Task Main(string[] args)
        {
            using (ChromeDriver? chromeDriver = new()) //using statement for automatic disposal
            {
                await Task.Delay(3000); //allow chrome to open for some short time
                chromeDriver.Quit(); //quit to fully close browser and release resources
            }
        }
    }
}
using OpenQA.Selenium.Chrome;
namespace ChromeBotTest
{
    public class Program
    {
        private static async Task Main(string[] args)
        {
            ChromeDriver chromeDriver = new(); 
            await Task.Delay(3000); //allow chrome to open for some short time
            chromeDriver.Quit(); //quit to fully close browser and release resources
        }
    }
}
using OpenQA.Selenium.Chrome;
namespace ChromeBotTest
{
    public class Program
    {
        private static async Task Main(string[] args)
        {
            ChromeDriver? chromeDriver = new();
            await Task.Delay(3000); //allow chrome to open for some short time
            chromeDriver.Close(); //close all windows
            chromeDriver.Quit(); //quit to fully close browser and release resources
            chromeDriver?.Dispose(); //dispose incase chromeDriver is not properly disposed
            chromeDriver = null; //set to null to ensure its cleared
        }
    }
}
    using OpenQA.Selenium.Chrome;
    namespace ChromeBotTest
    {
        public class Program
        {
            private static async Task Main(string[] args)
            {
                ChromeDriver? chromeDriver = new();
                await Task.Delay(3000); //allow chrome to open for some short time
                chromeDriver.Close(); //close all windows
                chromeDriver.Quit(); //quit to fully close browser and release resources
                chromeDriver?.Dispose(); //dispose incase chromeDriver is not properly disposed
                chromeDriver = null; //set to null to ensure its cleared
                await Task.Delay(2000); //allow 2 second delay
                GC.Collect();
                GC.WaitForPendingFinalizers(); //clean up garabage collection
                GC.Collect();
                await Task.Delay(10000); //wait 10 seconds to allow garbage collection sufficent time to execute
            }
        }
    }

просто используйте driver.quit()... пусть GC выполнит свою работу автоматически. Сначала вам не нужен .close() (в основном для закрытия одной вкладки, если у вас их несколько), не нужно удалять или использовать с помощью... не нужно вручную использовать GC. Я бы сравнил ваши результаты с ручным запуском и закрытием браузера (подозреваю, что это все связано с браузером). Единственное отличие от сеанса Selenium по умолчанию заключается в том, что временные каталоги/профили/файлы создаются и затем очищаются при выходе. Логика GC браузера довольно сложна, и трудно предсказать, когда она будет иметь дело с вещами, помеченными для удаления.

browsermator 09.08.2024 19:06

Это не исправлено - обновлено начальное сообщение тестового кода.

freyfrey01 09.08.2024 20:55

Этот драйвер Chrome запускается в отдельном процессе? Или память увеличивается при повторном запуске процесса? Обычно я бы советовал использовать профилировщик памяти, но он в основном полезен для поиска управляемых утечек. Я подозреваю, что из-за этого происходит утечка неуправляемой памяти, и никакая сборка мусора не поможет. Но мне непонятно, в чем проблема, если ты распоряжаешься всем, что создал, то твои обязанности должны быть выполнены.

JonasH 12.08.2024 12:47
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
63
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Если это не вызывает проблем с производительностью, я не уверен, что вам нужно решить проблему. .NET Framework не освобождает память только потому, что работает сборщик мусора. Среда выполнения сохранит выделенную память, поэтому операционная система по-прежнему будет видеть, что эта память используется (хотя она может считаться неактивной). Только если в системе закончится неиспользуемая память, среда выполнения .NET начнет отказываться от неиспользуемой памяти. Это ожидаемое поведение, если я правильно помню.

Некоторые дополнительные ресурсы:

Честно говоря, цифры, которые вы выделили на скриншотах, не сильно отличаются, если я чего-то не упускаю...

переход от 7000 КБ до 50 000 КБ за 5 минут — это большая разница. Если так будет продолжаться в течение 24 часов, это увеличение составит почти 12384000 КБ или около 12 ГБ. Это вызывает серьезную проблему с производительностью, поскольку после недели работы машина использует более 80% оперативной памяти, которая просто не используется, но все еще считается «активной». Обычно я перезагружаю компьютер, когда он достигает 85%, поскольку другие процессы, такие как IIS, начинают замедляться.

freyfrey01 09.08.2024 20:54

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

browsermator 09.08.2024 21:50

Вы также можете проверить диспетчер задач на наличие потерянных процессов chromedriver.exe. Маловероятно, что это является причиной высокого использования памяти, но проверить стоит. (chromedriver в какой-то момент обязательно выйдет из строя, и не всегда изящно)

browsermator 09.08.2024 22:00

Спасибо за вклад @browsermator. Я подтвердил, что нет зависших/потерянных процессов chromedriver.exe или chrome.exe. Эта машина, показанная на скриншотах, имеет 96 ГБ оперативной памяти. В целях тестирования я проводил тесты на этой машине, но наша производственная машина имеет 144 ГБ ОЗУ, поэтому возникает та же проблема. У нас есть несколько других сценариев, которые запускаются автоматически с использованием .NET и не вызывают такого потребления ресурсов. Мы уже видели, как эта запись в RamMap превысила 90 ГБ.

freyfrey01 09.08.2024 22:16

@freyfrey01, это подозрительно. Мы запускаем наши тесты на Windows Server (что-то недавнее) и запускаем тесты в ночное время. У нас на этом сервере 16 ГБ ОЗУ, и он отлично справляется с 12-часовым тестовым запуском. Но мы проводим тесты партиями. Мы не проводим 4000 тестов на селен подряд. Тесты на селене мобилизуют множество машин. Возможно, вам просто нужно периодически убивать процессы.

Greg Burghardt 09.08.2024 22:30

вы можете проверить/обновить драйверы видеокарты... Я видел, что это упоминалось как причина этого, когда я гуглил. (rammap не знает, что использует эту память, поэтому она указана как «неиспользуемая»)

browsermator 09.08.2024 22:35

Единственное, о чем я могу думать, это диспетчер драйверов, потому что он выполняет некоторые операции по загрузке файлов ввода-вывода. (от таких вещей вы бы хотели избавиться, так что, возможно, они делают это неправильно). Вы можете попробовать явно указать пути к chromedriver и options.setBinary (путь к браузеру), чтобы исключить это.

browsermator 09.08.2024 22:52

Спасибо за комментарии @browsermator, я подтвердил, что это не драйверы видеокарты, используя приложение Microsoft Process Explorer, которое показывает использование оперативной памяти драйвера (упоминается на других форумах). Я также протестировал параметр setBinary (кажется, устаревший для options.BinaryLocation=...) options.BinaryLocation = @"C:\Program Files\Google\Chrome\Application\chrome.exe"; ChromeDriver chromeDriver = новый (варианты); который дал те же результаты

freyfrey01 09.08.2024 23:09

Я полагаю, что, возможно, стоит попробовать Geckodriver/Firefox, чтобы исключить какую-то ошибку в chromedriver. Если вы наберете IWebDriver, вы можете использовать тот же код, но для инициализации.

browsermator 09.08.2024 23:20

@browsermator попробовал использовать драйвер Firefox и получил те же результаты. Кто-нибудь способен воспроизвести?

freyfrey01 10.08.2024 04:37

Я думаю, что это, вероятно, исключает сторону Selenium. (но обязательно избавьтесь от всех вызовов обработки GC и удаления... пусть GC сделает это автоматически) Запуск 15 экземпляров ядра .NET + Selenium + веб-драйвера + браузера за одну минуту кажется многоватым. Запустите 1 экземпляр и убедитесь, что он корректно завершает работу через несколько минут. (вызывается команда выхода?) Поскольку ядро ​​.NET является собственной средой выполнения, возможно, в Rammap его ресурсы указаны как неиспользуемые.

browsermator 12.08.2024 20:55

может ли это быть так просто? Нет выхода()?

browsermator 12.08.2024 23:51
Ответ принят как подходящий

Отследил его до Trellix/Mcafee, удерживающих зомбированные дескрипторы завершенных процессов с помощью RamMap и https://github.com/randomascii/blogstuff/tree/main/FindZombieHandles

Другие вопросы по теме