HTMLLoader / StageWebView内存泄漏

时间:2014-07-08 22:07:07

标签: actionscript-3 air

创建StageWebView实例时,如果在多个站点之间切换,您会注意到所使用的内存量会慢慢增加。如果您正在查看2/3站点,这不是问题,但是当创建带有内置浏览器的AIR应用程序时,我现在在内存管理方面遇到了障碍

无论我做什么

  • 每次调用后的StageWebView.dispose()
  • 重置阶段

问题仍然存在。网页的每次加载都会使内存增加3mb,逐渐增加到1gb以上并使应用程序崩溃。

我在StageWebView实例上没有事件监听器。我完全没有提到它。第二个URL加载后,内存不会完全重置。

通过在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();
        }

    }

}

有没有人有这方面的经验?

2 个答案:

答案 0 :(得分:2)

我对您的代码进行了一些小的更改,使其可以与AIR 14 SDK(ASC 2.0,没有flex)兼容,我构建&用

测试空气应用

mxmlc -optimize=true +configname=air Controller.as && adl Controller.xml

并观看Adobe Scout的内存使用情况

GC稳定后的记忆,稳定在14481 kB

您可以向mxmlc添加-advanced-telemetry=true选项以精确跟踪分配/解除分配,只需考虑推送"隐藏已清理的对象"切换并选择两个GC之间的范围。你会看到一些物体没有立即释放,但你也会看到,如果你同时选择其中几个范围,那些临时泄漏就不会加起来,这就解释了为什么内存不会不断增加, GC环境,内存泄漏并不是真正的内存泄漏

如何衡量内存使用情况?

我检查了adl进程的内存使用情况,它更高(> 100MB)并且不太稳定,但在GC之后仍然稳定在110到120MB之间。在任何情况下,它肯定看起来不会增加到1 GB

请记住,ActionScript GC是惰性的并保留对象池以供将来重用,任何比hello world更多的应用程序有时会出现奇怪的内存行为,这是AVM2工作方式的一部分。

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();
        }

    }

}

答案 1 :(得分:0)

这只是一个随意的路人的想法 ramblings ..注意:我没有测试运行你的代码,因为此时我还没有访问Flash。我也不会100%支持我的漫步(我知道这是一个WTF?但请继续阅读,希望在那里有用的东西......)

在你的函数tryGC(e:MouseEvent):void中设置stageWeb = null;这很好,但是在你的函数load(e:MouseEvent):void中你说“如果stageWeb为null则再创建一个”。好吧,因为你以前设置Flash考虑一个null它乖乖地在内存中创建一个新的(旧的内存印记仍然可能还没有垃圾收集(只是在它的队列中)因此它看起来像ram使用已经增加。

如果您之后要立即加载其他页面,那么整个tryGC事情就有点瑕疵。垃圾收集(可能)从未发生过,因为周围还有一个鼠标事件,if (!stageWeb) { //etc }所以基本上你将其状态转换为null,但它无法被真正删除,因为其他东西正在使用引用它可以在将来的任何时候被调用,所以它只是在内存中存在(具有null状态)..

可能的解决方案:

1)在您的函数tryGC中删除加载网址的鼠标侦听器

private function tryGC(e:MouseEvent):void {
stageWeb.stage = null;
stageWeb.viewPort = null;
stageWeb.dispose();
stageWeb = null;
btn.removeEventListener(MouseEvent.CLICK, load);
System.gc();
}

现在等几秒钟检查ram的使用量是否减少了?如果在尝试GC之前它有助于加载至少10个以上的网址,因为您无法通过点击加载网址。这仅用于测试以查看mouselistener是否是垃圾收集器的问题

2)您真正需要的是清除/覆盖当前网址内容/缓存的方法...不重新创建新的HTML网页浏览量。在这种情况下,这没有任何帮助。我认为你的问题也源于此... History Forward/Back ...我怀疑每个网址负载都会添加到历史记录中,而历史记录又更像是一个“内容缓存”而不仅仅是“以前的字符串” URL”。
考虑一下您对BAT文件的计划......那么打开100个网址,依次缓存它们,但您的视口一次只显示一个项目(其他99个页面保存在内存中,满载并准备就绪用于即时显示..)。我不知道解决方法。没有stageWeb.unloadURL();甚至stageWeb.clearCache();我看起来不够努力,但是这个星球上的某个人知道一个关于它的博客(并且希望)......