《编译原理》讲程序运行时的内存分配策略有静态、栈式和堆式三种。 静态存储分配是指在编译时就能确定每个数据目标在运行时刻的存储空间需求,要求程序代码中不允许有可变数据结构的存在;栈式存储分配也可称为动态存储分配,程序对数据区的需求在编译时是完全未知的,只有到运行的时候才能够知道,按照先进后出的原则进行分配;而堆式存储分配则专门负责在编译时或运行时模块入口处都无法确定存储要求的数据结构的内存分配,比如可变长度串和对象实例,堆由大片的可利用块或空闲块组成,堆中的内存可以按照任意顺序分配和释放。
在编程中,例如C/C++中,所有的方法调用都是通过栈来进行的,所有的局部变量,形式参数都是从栈中分配内存空间的。堆是应用程序在运行的时候请求操作系统分配给自己内存,由于从操作系统管理的内存分配,所以在分配和销毁时都要占用时间,因此用堆的效率非常低。但是堆的 优点在于,编译器不必知道要从堆里分配多少存储空间,也不必知道存储的数据要在堆里停留多长的时间,因此,用堆保存数据时会得到更大的灵活性。事实上,面 向对象的多态性,堆内存分配是必不可少的,因为多态变量所需的存储空间只有在运行时创建了对象之后才能确定。
AVM2是一种基于堆栈的用于解释ABC字节码的虚拟机,使用JIT编译器技术以提高性能。注意Java作为另一种解释型语言,也使用了JIT编译技术。在AVM2中,写在构造函数里的语句是不会被JIT编译,所以高性能的程序应当将初始化程序写进另外一个单独的初始化函数里。
AVM2执行的对象(输入,Input)就是我们创建的一个一个编译后的Actionscript Class文件。AVM2为运行一个程序定义了几种数据区,包括:堆、函数信息( method information)、本地数据域(local data area, 由堆栈和pc寄存器组成),常量池(constant Pool)以及运行时环境(run-time environment)。操作栈( operand stack)、域栈( scope stack)、pc寄存器(local registers)组成了本地数据域。AVM2操作栈堆栈和C这样的传统编程语言中的堆栈是类似的,它保存局部变量和部分结果,并且在方法调用和返回中也担任一些职责。域栈会影响变量的作用域。使用Try、with或者创建闭包时候会影响该栈。
AVM2中堆用于所有的类实例创建。堆所存储的对象被一个自动存储管理系统回收(也就是我们所熟知的垃圾收集器(gc))。对象不能被显式的释放,AVM2假设没有特定类型的自动存储管理系统,存储管理技术可以根据实现者的系统需求进行选择。
Common Subexpression Elimination (CSE)通用子表达式消除是一种优化技术,它可以识别重复的表达式并重用它的值,而不是再次执行相应的计算。有了它,在编写代码的时候就可以不必太多考虑表达式的问题,而将优化工作交给编译器。AVM2、JVM、.NET都采用了这种技术,但是在AVM2中,这种技术并不是很完善。所以类似这样的语句“for (var i:int=0; i < arr.length; i++)”性能就不是很好。
Java中,StringBuffer类可以优化字符串操作。http://www.onflex.org/ACDS/AS3TuningInsideAVM2JIT.pdf解释说Flash曾经也考虑过写一个类似StringBuffer的类,但是最终Flash重载了"+"操作符,所以不必担心字符串的"+"带来的性能损耗。Textfield的appendText方法性能更好一些。
有关类的实现细节可以参考:http://www.onflex.org/ACDS/AS3TuningInsideAVM2JIT.pdf与http://www.adobe.com/devnet/actionscript/articles/avm2overview.pdf。