Skip to content

Instantly share code, notes, and snippets.

@rzubek
Last active December 19, 2015 05:19
Show Gist options
  • Save rzubek/5903380 to your computer and use it in GitHub Desktop.
Save rzubek/5903380 to your computer and use it in GitHub Desktop.
Very simple memory scanner class, keeps track of memory allocs and deletes between invocations

Very simple memory scanner class, that keeps track of:

  1. how much memory was allocated and freed between invocations
  2. for objects allocated but not freed, prints out detailed information about what they are

Usage example:

  if (_mem.isRunning) {
		_mem.toggle(); // turn off first
	}
			
	Logger.debug(_mem.lastMessage);
			
	_mem.toggle(); // turn back on

Creates output like this:

[ DEBUG ] memory scanner: off
alloc'd 86 objects
deleted 86 objects, 2464 bytes
allocations by type:
 flash.events::Event => 17
 String => 63
 flash.events::TimerEvent => 3
 starling.events::TouchEvent => 1
 __AS3__.vec::Vector.<*> => 1
 Function => 1
unclaimed 5 objects, size 472 bytes
 leaked 40 b : [Event type="enterFrame" bubbles=false cancelable=false eventPhase=2]
 leaked 32 b : DirectX9
 leaked 40 b : [TimerEvent type="timer" bubbles=false cancelable=false eventPhase=2]
 leaked 56 b : memory scanner: off
 leaked 304 b : function Function() {}
package
{
import flash.sampler.*;
import flash.system.System;
import flash.utils.Dictionary;
import flash.utils.getQualifiedClassName;
/**
* Tracks memory allocation / deallocation between two points in time,
* and prints out information about leaks
*/
public class MemoryScanner
{
/** If true, a run has been started, and not yet finished. */
private var _running :Boolean = false;
/** Number of objects allocated during the last run */
public var newCount :int;
/** Number of objects deleted during the last run */
public var deleteCount :int;
/** Total deletion size from the last run, in bytes */
public var deleteSize :int;
/** Number of objects allocated but not garbage collected during the last run */
public var leakedCount :int;
/** Total leakage size from the last run, in bytes */
public var leakedSize :int;
/** Internal storage for deleted objects */
private var _deleted :Vector.<DeleteObjectSample> = new Vector.<DeleteObjectSample>();
/** Internal storage for leaked objects */
private var _leaked :Vector.<NewObjectSample> = new Vector.<NewObjectSample>();
/** Last message */
private var _lastMessage :String;
public function MemoryScanner() {
setSamplerCallback(samplerCallback);
}
/** True if a run has been started, and not yet finished. */
public function get isRunning () :Boolean {
return _running;
}
/** Returns the last message from the memory scanner */
public function get lastMessage () :String {
return _lastMessage;
}
/** Starts or stops the run */
public function toggle () :void {
_lastMessage = ("memory scanner: " + (_running ? "off\n" : "on\n"));
toggleHelper(! _running);
}
/** Starts or stops the scan. */
private function toggleHelper (start :Boolean) :void {
System.gc();
if (start) {
clearSamples();
startSampling();
} else {
pauseSampling();
samplerCallback();
stopSampling();
clearSamples();
}
_running = start;
}
private function samplerCallback (... args) :void {
var samples :* = getSamples();
newCount = 0;
deleteCount = 0;
deleteSize = 0;
leakedCount = 0;
leakedSize = 0;
_leaked.length = 0;
_deleted.length = 0;
var types :Dictionary = new Dictionary();
for each (var sample :* in samples) {
if (sample is NewObjectSample) {
newCount++;
if (sample.type) {
var current :uint = uint(types[sample.type]);
types[sample.type] = current + 1;
}
if (sample.object) {
leakedCount++;
leakedSize += sample.size;
_leaked.push(sample);
}
} else if (sample is DeleteObjectSample) {
deleteCount++;
deleteSize += sample.size;
_deleted.push(sample);
} else {
// trace("sample: " + sample);
}
}
// see what remains
_lastMessage += ("alloc'd " + newCount + " objects\n");
_lastMessage += ("deleted " + deleteCount + " objects, " + deleteSize + " bytes\n");
_lastMessage += "allocations by type:\n";
for (var key :* in types) {
var name :String = getQualifiedClassName(key);
_lastMessage += (" " + name + " => " + types[key] + "\n");
}
_lastMessage += ("unclaimed " + leakedCount + " objects, size " + leakedSize + " bytes\n");
for each (var leaked :NewObjectSample in _leaked) {
try {
var desc :String = String(leaked.object);
} catch (e:Error) {
desc = e.message;
}
_lastMessage += (" leaked " + leaked.size + " b : " + desc + "\n");
}
types = null;
_leaked.length = 0;
_deleted.length = 0;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment