When creating an instance of StageWebView, if you switch between multiple sites you'll notice that the amount of memory used slowly goes up. This isn't an issue if you're viewing 2/3 sites, but when it comes to creating an AIR application with a built in browser, I've now hit a brick wall in terms of memory management.
No matter what I do
The problem persists. Each load of a web page will increase memory by 3mb, which gradually increases to over 1gb and crashes the application.
I have no event listeners on the StageWebView instance. I hold absolutely no references to it. The memory just does not fully reset after the 2nd URL loads.
This can be seen by running the below in AIR:
package kazo
{
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
import flash.media.StageWebView;
import mx.controls.Button;
import mx.core.UIComponent;
import flash.system.System;
/**
* ...
* @author KM
*/
public class Controller extends UIComponent
{
private var stageWeb:StageWebView;
private var url:uint = 0;
private const URL_ARRAY:Array = [
'http://www.mmo-champion.com/',
'http://www.bbc.co.uk/news/',
'http://www.twitch.tv/riotgames/',
'http://www.stackoverflow.com/'
]
/**
*
*/
public function Controller()
{
addEventListener(Event.ADDED_TO_STAGE, init);
}
/**
*
* @param e
*/
private function init(e:Event):void {
removeEventListener(Event.ADDED_TO_STAGE, init);
stageWeb = new StageWebView();
stageWeb.stage = stage;
stageWeb.viewPort = new Rectangle(0, 80, width, height - 50);
var btn:Button = new Button();
addChild(btn);
btn.label = 'Load URL';
btn.x = 0;
btn.y = 10;;
btn.width = 100;
btn.height = 30;
btn.addEventListener(MouseEvent.CLICK, load);
btn = new Button();
btn.label = 'Try to GC';
btn.x = 150;
btn.y = 10;;
btn.width = 100;
btn.height = 30;
addChild(btn);
btn.addEventListener(MouseEvent.CLICK, tryGC);
/// 26,576k
}
/**
*
* @param e
*/
private function load(e:MouseEvent):void {
if (!stageWeb) {
stageWeb = new StageWebView();
stageWeb.stage = stage;
stageWeb.viewPort = new Rectangle(0, 80, width, height - 50);
}
stageWeb.loadURL(URL_ARRAY[url % 4]);
url++;
}
/**
*
* @param e
*/
private function tryGC(e:MouseEvent):void {
stageWeb.stage = null;
stageWeb.viewPort = null;
stageWeb.dispose();
stageWeb = null;
System.gc();
}
}
}
Does anyone have any experience with this?
I did some minor changes to your code to make it compilable with AIR 14 SDK (ASC 2.0, no flex), I build & tested the air application with
mxmlc -optimize=true +configname=air Controller.as && adl Controller.xml
and watched memory usage with Adobe Scout
Memory after GC was stable, stabilizing at 14481 kB
You may add -advanced-telemetry=true option to mxmlc to track allocations/deallocations precisely, just think to push the "hide cleaned objects" switch and select a range beetween two GC. You will see some object are not released immediatly, but you'll also see that if you select several of those ranges at the same time, those temporary leaks do not add up, which explains why memory does not increase constantly, in a GC environment, memory leaks are not really memory leaks
How do you measure memory usage ?
I checked memory usage of the adl process, it was higher ( > 100MB ) and less stable, but still stabilizing somewhere between 110 and 120MB after GC. In any case, it was definitely not looking like it was going to increase up to 1 GB
Just remember that ActionScript GC is lazy and keeps pool of objects for future reuse, any application doing more than hello world will have sometimes strange memory behavior, which is part of the way AVM2 is working.
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
import flash.media.StageWebView;
import flash.system.System;
import flash.text.TextField;
/**
* ...
* @author KM
*/
public class Controller extends Sprite
{
private var stageWeb:StageWebView;
private var url:uint = 0;
private const URL_ARRAY:Array = [
'http://www.mmo-champion.com/',
'http://www.bbc.co.uk/news/',
'http://www.twitch.tv/riotgames/',
'http://www.stackoverflow.com/'
]
/**
*
*/
public function Controller()
{
addEventListener(Event.ADDED_TO_STAGE, init);
}
/**
*
* @param e
*/
private function init(e:Event):void {
removeEventListener(Event.ADDED_TO_STAGE, init);
stageWeb = new StageWebView();
stageWeb.stage = stage;
stageWeb.viewPort = new Rectangle(0, 80, stage.stageWidth, stage.stageHeight - 50);
var btn:TextField = new TextField();
btn.selectable = false;
addChild(btn);
btn.text = 'Load URL';
btn.x = 0;
btn.y = 10;;
btn.width = 100;
btn.height = 30;
btn.addEventListener(MouseEvent.CLICK, load);
btn = new TextField();
btn.selectable = false;
btn.text = 'Try to GC';
btn.x = 150;
btn.y = 10;;
btn.width = 100;
btn.height = 30;
addChild(btn);
btn.addEventListener(MouseEvent.CLICK, tryGC);
/// 26,576k
}
/**
*
* @param e
*/
private function load(e:MouseEvent):void {
if (!stageWeb) {
stageWeb = new StageWebView();
stageWeb.stage = stage;
stageWeb.viewPort = new Rectangle(0, 80, stage.stageWidth, stage.stageHeight - 50);
}
stageWeb.loadURL(URL_ARRAY[url % 4]);
url++;
}
/**
*
* @param e
*/
private function tryGC(e:MouseEvent):void {
stageWeb.stage = null;
stageWeb.viewPort = null;
stageWeb.dispose();
stageWeb = null;
System.gc();
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With