19

I am using PhantomJS via Selenium and encountered a problem on one website with a lot of images.

When I am trying to take screenshot PhantomJS process memory consumption gets very high, ≈400-450 MB (≈100 MB before screenshot)

With --load-images=no it is better, ≈70-100 MB.

Is there any way to solve this issue without disabling images completely? Maybe it is possible to take screenshot of visible area only instead of full page?

With other WebDrivers (such as Chrome) it is working fine.

class Program
{
    public static RemoteWebDriver CreatePhantomJsDriver()
    {
        var service = PhantomJSDriverService.CreateDefaultService();
        //service.AddArgument("--load-images=no");

        var options = new PhantomJSOptions();
        options.AddAdditionalCapability("phantomjs.page.settings.userAgent", "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36");

        return new PhantomJSDriver(service, options);
    }

    public static void SaveScreenshot(RemoteWebDriver driver)
    {
        try
        {
            driver.TakeScreenshot().SaveAsFile(DateTime.Now.Ticks + ".jpg", ImageFormat.Jpeg);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    static void Main(string[] args)
    {
        using (var driver  = CreatePhantomJsDriver())
        {
            driver.Manage().Window.Size = new Size(1600, 1200);

            driver.Url = "http://color-looks.ru/index";

            Thread.Sleep(5000);

            SaveScreenshot(driver);

            Thread.Sleep(5000);
        }
    }
}

(it is not the website I was using because it requires login/password auth, just some image heavy website from Google, memory consumption is a bit lower but still huge — ≈300 MB)

Alex P.
  • 3,697
  • 9
  • 45
  • 110
  • You could set the [`page.clipRect`](http://phantomjs.org/api/webpage/property/clip-rect.html), but I don't know if it would be useful to you and whether you can set it in the same way you set the user agent string. – Artjom B. Aug 31 '15 at 09:17
  • Have you seen http://stackoverflow.com/questions/8647980/how-to-reduce-phantomjs-memory-consumption ? – Nathan Sep 10 '15 at 04:10
  • @Nathan, I don't see how this can help me. I don't want to close the page immediately after taking the screenshot, and when I close it (by disposing the driver object) the memory is released fine. It is also decreased back to normal if I go to another page/URL. My problem is that memory consumption highly increases when I take screenshot on a page, and because of that I cannot use multiple (5-8) instances of PhantomJS at the same time (not enough memory on the machine I use for that). – Alex P. Sep 10 '15 at 04:55
  • @Alex11223 I've tried to fiddle a little with the code and the page you've provided produces a peak of 66mb, varrying a little with each page load. As Nathan suggests, the GC isn't collecting everything so enforcing it to collect right after `SaveScreenshot` does lower the memory consumption back down to some 20mb and keeps it there no matter the screenshot count. For comparison, a Chrome tab uses 230mb to render the page, so I think you're well off here. What do you mean with 'With other WebDrivers (such as Chrome) it is working fine' btw? – Heki Sep 10 '15 at 06:39
  • @Heki I don't see such high memory usage with Chrome webdriver instead of PhantomJS. I have 2 Chrome processes (main process and tab process), 45-50 MB each. Are you sure it uses 230 MB? – Alex P. Sep 10 '15 at 08:22
  • @Alex11223 Yeah, 222,204K memory (this time) used by the tab that displays http://color-looks.ru/index – Heki Sep 10 '15 at 08:29
  • @Heki oh, I checked again with ProcExp, indeed it is about 250 MB. http://i.imgur.com/2bzHQkw.png But it is not tab process, the tab process is the last one and it uses 70 MB. – Alex P. Sep 10 '15 at 11:24
  • Anyway, I think PhantomJS uses much more memory because it (or webdriver?) renders the whole page when taking screenshot while Chrome webdriver takes screenshot only of the visible part. My original page is very long and I need only upper part. I tried `options.AddAdditionalCapability("phantomjs.page.clipRect", "{ top: 0, left: 0, width: 1000, height: 900 }");` as suggested above, but it didn't work, it still takes screenshot of the whole page. – Alex P. Sep 10 '15 at 11:25
  • Oh, okay. Have you tried this? http://stackoverflow.com/questions/11917042/how-to-render-part-of-a-page-with-phantomjs – Heki Sep 10 '15 at 11:34
  • Yes. I tried to set `page.clipRect` in PhantomJS script without Selenium/webdriver, it worked, it used much less memory. I was looking for a way to do this in PhantomJS webdriver for Selenium. – Alex P. Sep 10 '15 at 14:26
  • @Alex11223 I suppose it's one and the same, I need to brush off my reading skills.. Sorry. I hope you find a way to fix your problem. I'll fav this question – Heki Sep 11 '15 at 06:22

2 Answers2

1

The method TakeScreenshot returns a Bitmap object. The code sample you provided does not dispose of this object, so the GDI object is hanging around in memory for a long time (possibly indefinitely).

Change your code to this:

public static void SaveScreenshot(RemoteWebDriver driver)
{
    try
    {
        using(var screenshot = driver.TakeScreenshot())
        {
            screenshot.SaveAsFile(DateTime.Now.Ticks + ".jpg", ImageFormat.Jpeg);
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}
brianfeucht
  • 774
  • 8
  • 21
0

try this one:

Screenshot ss = ((ITakesScreenshot)driver).GetScreenshot();
ss.SaveAsFile(DateTime.Now.Ticks + ".jpg", ImageFormat.Jpeg);
Thiago Custodio
  • 17,332
  • 6
  • 45
  • 90