C # Большая утечка памяти в GeckoFX с HtmlAgilityPack - PullRequest
1 голос
/ 29 апреля 2019

Я пишу программу для очистки множества веб-сайтов компаний (до 100 000) для получения актуальной контактной информации, а также некоторой информации об их сфере деятельности в C #. Поскольку большинство веб-сайтов не могут отображаться в обычном веб-браузере .NET, я использую geckofx для перехода на эти веб-сайты и для поиска релевантного мне контента, я выбираю узлы с помощью HtmlAgilityPack.

Процесс всегда один и тот же: если у меня есть URL-адрес для компании, я сразу захожу на веб-сайт, в противном случае я использую bing для поиска веб-адреса (Google, похоже, не нравится, когда он используется автоматически). На веб-сайте я ищу ссылку на выходные данные и ссылки на страницы, которые могут указывать на некоторую область деятельности, я перехожу к этим ссылкам и ищу ключевые фразы, которые я указал заранее. Все работает синхронно, я жду, пока браузер вызовет событие DocumentCompleted каждый раз.

Пример:

//I navigate to bing looking for my company's name and postal code
Variables.browser.Navigate("https://www.bing.com/search?q=" + c.Name.Replace(" ", "+") + "+" + c.Zip.Replace(" ", "+"));

//I wait for the browser to finish loading. The Navigating event sets BrowserIsReady to false and the DocumentCompleted event sets it to true
do
{
    f.Application.DoEvents();
} while (!Variables.BrowserIsReady);

HtmlDocument browserDoc = new HtmlDocument();
browserDoc.LoadHtml(Variables.browser.Document.Body.OuterHtml);

//I select the relevant node in the document
HtmlNode sidebarNode = browserDoc.DocumentNode.SelectSingleNode("//div[contains(concat(\" \", normalize-space(@class), \" \"), \" b_entityTP \")]");

if (sidebarNode != null)
{
    Variables.logger.Log("Found readable sidebar. Loading data...");
    string lookedUpName, lookedUpStreet, lookedUpCity, lookedUpZip, lookedUpPhone, lookedUpWebsite;

    HtmlNode infoNode = sidebarNode.SelectSingleNode("//div[contains(concat(\" \", normalize-space(@class), \" \"), \" b_subModule \")]");

    HtmlNode nameNode = infoNode.SelectSingleNode("//div[contains(concat(\" \", normalize-space(@class), \" \"), \" b_feedbackComponent \")]");
    if (nameNode != null)
    {
        string[] dataFacts = nameNode.GetAttributeValue("data-facts", "").Replace("{\"", "").Replace("\"}", "").Split(new string[] { "\",\"" }, StringSplitOptions.None);
        foreach (string dataFact in dataFacts)
        {
            //... abbreviated
        }
    }
    //And at the end of every call to a node object I set it back to null
    nameNode = null;
}

Мой geckofx не может записывать кеш в память или загружать изображения с веб-сайтов, которые я установил с помощью

GeckoPreferences.Default["browser.cache.memory.enabled"] = false;
GeckoPreferences.Default["permissions.default.image"] = 2;

перед созданием моего экземпляра GeckoWebBrowser.

После каждого очищенного сайта я звоню

//CookieMan is used as a global variable so I don't have to recreate it every time.
private static nsICookieManager CookieMan;
//...
CookieMan = Xpcom.GetService<nsICookieManager>("@mozilla.org/cookiemanager;1");
CookieMan = Xpcom.QueryInterface<nsICookieManager>(CookieMan);
CookieMan.RemoveAll();
Gecko.Cache.ImageCache.ClearCache(true);
Gecko.Cache.ImageCache.ClearCache(false);
Xpcom.GetService<nsIMemory>("@mozilla.org/xpcom/memory-service;1").HeapMinimize(true);

для удаления файлов cookie, кэша изображений (который, я не уверен, даже создан) и для минимизации использования памяти Xulrunners.

Тем не менее, после довольно приятного запуска с приблизительным временем выполнения 2-3 секунды на запись и комфортным использованием памяти в 200-300 МБ, оба быстро увеличивают расход до 16-17 секунд на запись и более 2 ГБ используемой памяти только для моего сканера после 1 час.

Я пытался форсировать сборку мусора с помощью GC.Collect(); (что, я знаю, вы не должны этого делать) и даже перерабатывать весь объект браузера, останавливая, удаляя и создавая его заново, чтобы попытаться избавиться от неиспользуемого мусора в памяти , но безрезультатно. Я также пытался выключить Xulrunner и запустить его снова, но Xpcom.Shutdown(), кажется, останавливает все приложение, поэтому я не смог этого сделать.

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

1 Ответ

1 голос
/ 29 апреля 2019

Вы пытались использовать переработанные домены приложений?

AppDomain workerAppDomain = AppDomain.CreateDomain("WorkerAppDomain");
workerAppDomain.SetData("URL", "https://stackoverflow.com");
workerAppDomain.DoCallBack(() =>
{
    var url = (string)AppDomain.CurrentDomain.GetData("URL");
    Console.WriteLine($"Scraping {url}");
    var webClient = new WebClient();
    var content = webClient.DownloadString(url);
    AppDomain.CurrentDomain.SetData("OUTPUT", content.Length);
});
int contentLength = (int)workerAppDomain.GetData("OUTPUT");
AppDomain.Unload(workerAppDomain);
Console.WriteLine($"ContentLength: {contentLength:#,0}");

Выход:

Соскоб https://stackoverflow.com
ContentLength: 262.013

Данные, передаваемые между основным доменом приложений и рабочим доменом приложений, должны быть сериализуемыми.


Обновление: Однако наиболее чистым решением должно быть использование отдельных процессов. Это будет гарантировать, что утечка может быть надежно устранена.

...