我正在分析游戏项目中意外的内存泄漏,并发现了一些奇怪的结果。我正在使用Adobe Scout进行分析,并消除了所有其他因素,如椋鸟,纹理或我们的加载库。我将代码简化为简单加载png并立即在其complete事件上分配一个空的内联函数。
加载png会在默认情况下分配图像,如果在加载gc后什么都不做,则会清除该图像。但是创建一个内联函数似乎可以防止该图像以某种方式被垃圾收集。我的测试代码是;
public class Main extends Sprite
{
private var _callbacks:Array = new Array();
public function Main()
{
load("map.png", onPngLoaded);
}
private function onPngLoaded(bitmap:Bitmap):void
{
_callbacks.push(function():void { });
}
public function load(url:String, onLoaded:Function):void
{
var loader:Loader = new Loader;
var completeHandler:Function = function(e:Event):void {
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, completeHandler);
onLoaded(loader.content);
}
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
loader.load(new URLRequest(url));
}
}
如果删除了创建内联函数的代码;
private function onPngLoaded(bitmap:Bitmap):void
{
// removed the code here!
}
gc可以从内存中清除图像。
由于对此没有合理的解释,我怀疑是flash / as3错误。我很乐意听到任何测试我的代码并获得相同结果的评论。
注意:要测试,请用我的代码和导入包替换空as3项目的主类。你可以加载任何png。我正在使用flashdevelop,flex-sdk 4.6.0和flash player 14.
答案 0 :(得分:2)
创建内联函数时,所有局部变量都会与全局范围一起存储。所以在这种情况下,那将包括bitmap
参数。
有关详细信息,请参阅: http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7f54.html
以下是相关部分:
每当函数开始执行时,都会创建许多对象和属性。首先,创建一个称为激活对象的特殊对象,它存储参数以及在函数体中声明的任何局部变量或函数。其次,创建一个范围链,其中包含Flash Player或Adobe AIR的有序对象列表检查标识符声明。每个执行的函数都有一个存储在内部属性中的作用域链。对于嵌套函数,作用域链以其自己的激活对象开始,后跟其父函数的激活对象。链以这种方式继续,直到它到达全局对象。
这是在大多数情况下最好避免使用内联/匿名函数的另一个原因。
答案 1 :(得分:1)
所以使用asc2,Flash / Air 19:是的,我得到的结果与你看到的相同,但是由于匿名函数持有全局引用,我预计(就像我原来的评论所述)。
我根据Adobe的GC技术文章和公告以我的风格重写了它,并且没有泄漏,因为所有全局引用都被删除了。
剪切/粘贴AIR示例:
package {
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.display.Sprite;
import flash.display.Bitmap;
import flash.display.Loader;
import flash.events.Event;
import flash.net.URLRequest;
import flash.system.System;
import flash.utils.Timer;
import flash.events.TimerEvent;
public class Main extends Sprite {
var timer:Timer;
var button:CustomSimpleButton;
var currentMemory:TextField;
var highMemory:TextField;
var hi:Number;
var _callbacks:Array = new Array();
public function Main() {
button = new CustomSimpleButton();
button.addEventListener(MouseEvent.CLICK, onClickButton);
addChild(button);
currentMemory = new TextField();
hi = System.privateMemory;
currentMemory.text = "c: " + hi.toString();
currentMemory.x = 100;
addChild(currentMemory);
highMemory = new TextField();
highMemory.text = "h: " + hi.toString();
highMemory.x = 200;
addChild(highMemory);
timer = new Timer(100, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, timerHandler);
timer.start();
}
function timerHandler(e:TimerEvent):void{
System.pauseForGCIfCollectionImminent(.25);
currentMemory.text = "c: " + System.privateMemory.toString();
hi = System.privateMemory > hi ? System.privateMemory : hi;
highMemory.text = "h: " + hi.toString();
timer.start();
}
function onClickButton(event:MouseEvent):void {
for (var i:uint = 0; i<100; i++) {
//load("foobar.png", onPngLoaded);
load2("foobar.png");
}
}
private function onPngLoaded2(bitmap:Bitmap):void {
var foobarBitMap:Bitmap = bitmap; // assuming you are doing something
foobarBitMap.smoothing = false; // with the bitmap...
callBacks(); // not sure what you are actually doing with this
}
private function callBacks():void {
_callbacks.push(function ():void {
});
}
public function completeHandler2(e:Event):void {
var target:Loader = e.currentTarget.loader as Loader;
// create a new bitmap based what is in the loader so the loader has not refs after method exits
var localBitmap:Bitmap = new Bitmap((target.content as Bitmap).bitmapData);
onPngLoaded2(localBitmap);
}
public function load2(url:String):void {
var loader2:Loader = new Loader;
loader2.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler2, false, 0, true);
loader2.load(new URLRequest(url));
}
}
}
import flash.display.Shape;
import flash.display.SimpleButton;
class CustomSimpleButton extends SimpleButton {
private var upColor:uint = 0xFFCC00;
private var overColor:uint = 0xCCFF00;
private var downColor:uint = 0x00CCFF;
private var size:uint = 80;
public function CustomSimpleButton() {
downState = new ButtonDisplayState(downColor, size);
overState = new ButtonDisplayState(overColor, size);
upState = new ButtonDisplayState(upColor, size);
hitTestState = new ButtonDisplayState(upColor, size * 2);
hitTestState.x = -(size / 4);
hitTestState.y = hitTestState.x;
useHandCursor = true;
}
}
class ButtonDisplayState extends Shape {
private var bgColor:uint;
private var size:uint;
public function ButtonDisplayState(bgColor:uint, size:uint) {
this.bgColor = bgColor;
this.size = size;
draw();
}
private function draw():void {
graphics.beginFill(bgColor);
graphics.drawRect(0, 0, size, size);
graphics.endFill();
}
}