« ガンダム00が唐突に終わってしまった件 | Home | 時間の管理でフガフガ »

Mar 312008

Event.ENTER_FRAMEをしているだけでメモリが浪費される??

ちょいとデバッグ用に使用メモリとFPSを表示する"debugger"クラスなるものを作ってみた。
で、色々試していると、FPSを算出するためにEvent.ENTER_FRAMEをまわしているだけでメモリが4KBぐらいずつ浪費されているっぽい。Event.ENTER_FRAMEのhandlerに渡されるeventをnullにしてみたりしたのだけど変わらない。

メモリ変移を描画するために(ダブルクリックでグラフ表示)時間を取得しているのだけど、それの桁が増えるからその分のメモリが浪費されているのかと思ったのだけど、どーもそうではないらしい。(メモリのチェックはTimerで行っている。)Event.ENTER_FRAMEのhandler内でフレームカウントさせている変数を変化させず、中を空にしていても増加していく。(微々たる量だけど)で、Event.ENTER_FRAMEをaddしなければ使用メモリは増加しない。(つまりメモリーチェックの方の関数系では特にメモリーは増加していないということになるかと・・。)

これはクリアできないものなのでしょうか??


以下ソース。

package img8.debug {
	import flash.display.DisplayObject;
	import flash.display.Graphics;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.TimerEvent;
	import flash.system.System;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;
	import flash.utils.Timer;
	import flash.utils.getTimer;
//
	public class Debugger extends Sprite{
		private var _tfTitle:TextField;
		private var _tfMem:TextField;
		private var _tfFPS:TextField;
		private var _timer:Timer;
		private var _count:uint = 0;
		private var _txf_default:TextFormat = new TextFormat("_sans", 10, 0xffffff, null, null, null, null, null, "right");
		private var _txf_red:TextFormat = new TextFormat("_sans", 10, 0xff0000);
		private var _txf_blue:TextFormat = new TextFormat("_sans", 10, 0x00ff00);
		private var _txf_tmp:TextFormat ;
		private var _interval:Number = 500;
		private var _time:int = 0;
		private var _timeTmp:int = 0;
		//
		private var _memory:Number = 0;
		private var _memoryMin:Number = Math.round(System.totalMemory *0.001);
		private var _memoryMax:Number = 0;
		private var _memoryTmp:Number = 0;
		//
		private var _fps:Number = 0;
		private var _fpsMax:Number = 0;
		//
		private var _history:Array = [];
		private var _iHistory:Shape;
		//
		public function Debugger(target:DisplayObject,aInterval:Number=500) :void
		{
			_init(target, aInterval);
		}
		private function _init(target:DisplayObject,aInterval:Number):Boolean
		{
			if (!target.stage) {
				throw(new Error("this Object has not added yet. please addChild this displayObject"));
				return false;
			}
			var root = target.stage.root as DisplayObject;
			var mine = target.root.parent as DisplayObject;
			var flgIsRoot = (root == mine);
			if (flgIsRoot) 
			{
				interval = aInterval;
				_buildFace();
				mouseChildren = false;
				buttonMode = true;
				mouseEnabled = true;
				doubleClickEnabled = true;
				//
				_time = flash.utils.getTimer();
				_timer = new Timer(_interval);
				_timer.addEventListener(TimerEvent.TIMER, _onCheck);
				_timer.start();
				//
				addEventListener(Event.ENTER_FRAME, _doFrame);
				addEventListener(MouseEvent.MOUSE_DOWN, _onStartDrag);
				addEventListener(MouseEvent.MOUSE_UP, _onStopDrag);
				addEventListener(MouseEvent.DOUBLE_CLICK, _showHistory);
			}
			return flgIsRoot;
		}
		private function _buildFace():void
		{
			//title
			_tfTitle = new TextField();
			_tfTitle.autoSize = TextFieldAutoSize.LEFT;
			_tfTitle.defaultTextFormat = _txf_default;
			_tfTitle.text = "img8.debugger";
			_tfTitle.selectable = false;
			addChild(_tfTitle);
			//memory
			_tfMem = new TextField();
			_tfMem.width = _tfTitle.width;
			_tfMem.autoSize = TextFieldAutoSize.RIGHT;
			_tfMem.defaultTextFormat = _txf_default;
			_tfMem.text = "used memory";
			_tfMem.selectable = false;
			_tfMem.y = _tfTitle.y + _tfTitle.height;
			addChild(_tfMem);
			//fps
			_tfFPS = new TextField();
			_tfFPS.width =  _tfTitle.width;
			_tfFPS.autoSize = TextFieldAutoSize.RIGHT;
			_tfFPS.defaultTextFormat = _txf_default;
			_tfFPS.text = "framerate";
			_tfFPS.selectable = false;
			_tfFPS.y = _tfMem.y + _tfMem.height;
			addChild(_tfFPS);
			//header
			var bodyWidth:Number = _tfTitle.width + 10;
			graphics.beginFill(0x000000);
			graphics.drawRect(0, 0, bodyWidth, _tfTitle.y+_tfTitle.height);
			graphics.endFill();
			//body
			graphics.beginFill(0x333333);
			var titleHeight:Number = _tfTitle.y+_tfTitle.height
			graphics.drawRect(0, titleHeight, bodyWidth, _tfFPS.y+_tfFPS.height-titleHeight);
			graphics.endFill();
		}
		private function _checkMem():void
		{
			_memoryTmp = Math.round(System.totalMemory *0.001);
			if (_memoryTmp > _memory) {
				_txf_tmp = _txf_red;
			}else if (_memoryTmp < _memory) {
				_txf_tmp = _txf_blue;
			}else {
				_txf_tmp = _txf_default;
			}
			_memory = _memoryTmp;
			_memoryMax = Math.max(_memory, _memoryMax);
			_tfMem.text = String( _memory) + "KB";
			_tfMem.setTextFormat(_txf_tmp);
		}
		private function _checkFPS() :void
		{
			_timeTmp = getTimer();
			_fps = Math.round(_count * 100000 / (_timeTmp - _time)) / 100;
			_fpsMax = Math.max(_fps, _fpsMax);
			_txf_tmp = _fps < stage.frameRate ? _txf_red : _txf_default;
			_tfFPS.text = String(_fps) + "FPS";
			_tfFPS.setTextFormat(_txf_tmp);
			_count = 0;
			_time = _timeTmp;
		}
		private function _doFrame(event:Event):void
		{
			++_count;
		}
		private function _onCheck(e:TimerEvent = null):void
		{
			_checkFPS();
			_checkMem();
			_storeHistory();
			if (_iHistory)_drawHistory();
		}
		private function _storeHistory():void
		{
			if (_history.push( { time:_time, fps:_fps, memory:_memory } ) > 100)_history.splice(0, _history.length - 100);
		}
		private function _onStartDrag(event:MouseEvent = null):void
		{
			startDrag(false);
		}
		private function _onStopDrag(event:MouseEvent = null):void
		{
			stopDrag();
		}
		private function _showHistory(event:MouseEvent = null):void
		{
			if (_iHistory) {
				removeChild(_iHistory);
				_iHistory = null;
			}else{
				_iHistory = new Shape();
				_iHistory.x = width + 10;
				_iHistory.y = 150;
				_drawHistory();
				addChild(_iHistory);
			}
		}
		private function _drawHistory():void
		{
			if (_iHistory) _iHistory.graphics.clear();
			var hisGra:Graphics = _iHistory.graphics;
			hisGra.beginFill(0x333333);
			hisGra.drawRect(0, 0, 250, -150);
			hisGra.endFill();
			//memory
			var memoryRange:Number = - 100 / (_memoryMax-_memoryMin);
			hisGra.lineStyle(0, 0xff0000);
			var i:uint = 0;
			hisGra.moveTo(0, (_history[i].memory-_memoryMin) * memoryRange);
			for (i = 1; i < _history.length; i++) hisGra.lineTo(i*2.5, (_history[i].memory-_memoryMin)*memoryRange);
			//fps
			var fpsRange:Number = - 100 / _fpsMax;
			hisGra.lineStyle(0, 0x00ff00);
			i = 0;
			hisGra.moveTo(0, _history[i].fps*fpsRange);
			for (i = 1; i < _history.length; i++) hisGra.lineTo(i*2.5, _history[i].fps*fpsRange);
		}
		public function set interval(aInterval:Number):void
		{
			_interval = aInterval;
			if(_timer)_timer.delay = _interval;
		}
		public function get interval():Number
		{
			return _timer.delay;
		}
	}
}

usage:

import img8.debug.Debugger;
var debug = new Debugger(this);
this.addChild(debug);
まぁ微小な量だけど気持ち悪い。

ちょっと作り直しました。
apeirophobia: img8.debug.Debuggerこちらです。

2 Comments

1、ENTER_FRAMEイベントが起こる度にnew Event()されている。
2、GCはメモリが一定量に達しないと実行されない。
と言うわけでメモリが溜まっていっている。
これは言語仕様上(? リスナーの仕様上か?)仕方ないことかと思われます。

>knbさん
はぃ、そうなんです、一応理解はしているのですが、見てる感じGCはほぼ発動してないです・・。何かしら動いている間は発動無いんじゃないかというぐらい発動しませんw
localCnnection2回叩きをやれば発動しますが・・。(デバッガプレイヤーだとgcで発動できますが・・。)

で、腑に落ちないのがTimerではメモリが増加していかないってことなんですよね・・。なんでかなーと・・。target対象がdisplayObjectになってるから、その参照がメモリ増加の原因ですかねぇ・・・。ってことはshapeに対して適応すればメモリ増加を抑えられる・・?のかしら・・・。
ちょっとやってみよー。

Leave a comment

Search and Archives