1.内存分配与释放
对象分配内存,一般叫做对象的实例化。在分配内存之前,必须已经编写了一个类。例如,在Java语言中,对象是引用数据类型,定义一个对象引用,相当于声明一个对象,声明不同于创建。声明对象,表示只分配了存储地址的存储器位置,还没有为其分配内存,即只是在内存中定义了一个某名字的引用,类似于C++中的指针,此时指向空(null)值,或者说,引用为空。一般情况下,调用new方法才能创建一个对象。如果声明的对象引用不指向任何对象,则这样的引用为“空引用”(null reference或null pointer);如果声明的对象引用存储了一个实际对象的地址,则称“引用指向一个对象”。
对象的生成比较简单,涉及的安全考虑也不多;与此相对应,对象的内存也有释放的过程,但是和生成相比,它与系统安全性的关系更大一些。
以C++为例,阐述对象在内存中的存储和释放情况。对象通常存放在3个内存区域。
1)全局/静态数据区:主要存放全局对象和静态对象,在该内存区的对象或成员,直到进程结束,才会释放内存。
2)堆:存在于堆中的数据,分配内存的方法一般是new/malloc,释放内存的方法是delete/free。对于这种对象,可以进行创建和销毁并精确控制。堆对象在C++中的使用非常广泛。
3)栈:一般保存的是局部对象或者局部变量,使用栈对象效率较高,程序员无须对其生存周期进行管理。
C++中,和对象释放内存相关的,一般是析构函数。析构函数的作用是释放对象申请的资源,代码如下:
析构函数通常由系统自动调用。在以下几种情况下系统会调用析构函数:
●全局对象在进程结束时。
●堆中对象进行delete/free操作时。
●栈中对象生命周期结束时,包括离开作用域、函数正常跳出或者抛出异常等。
在使用析构函数时,可以充分利用它的性质进行一些操作,特别对于栈中对象,由于析构函数调用是由系统自动完成的,所以可以利用这一特性,将一些需要随着对象销毁而必须释放的资源封装在析构函数里由系统自动完成销毁或释放,这些工作的典型案例如下:
●某些资源的释放。
●关闭文件等。(www.xing528.com)
利用栈对象的这一特性进行自动管理,可以避免由于编程时的遗漏而忘记进行某种操作。
在Java语言中,对象的释放相对简单一些。许多方面,Java类似于C++。但是,Java去除了析构函数,取而代之的是finalize()方法。finalize()与C++的析构函数并不完全一样,finalize()方法的调用并不是在对象的作用域结束之后马上进行,而是与Java的垃圾回收器紧密相关。当Java虚拟机已确定尚未终止的任何线程无法再通过任何方法访问此对象时,由对象的垃圾回收器调用此方法。对于任何给定对象,Java虚拟机最多只调用一次finalize()方法。
2.静态成员安全
在类中,数据成员可以分为静态成员和非静态成员两种。在成员的声明前面加上关键字static(静态)就能创建静态成员。如果一个成员变量被声明为static,此变量就是静态变量,如果一个成员方法被声明为static,此方法就是静态方法,静态成员能够在它的类的任何对象创建之前被访问,而不必引用任何对象。
静态成员变量存储在全局数据区,为该类的所有对象共享,不属于任何对象的存储空间,逻辑上所有对象都共享这一存储单元,对静态成员变量的任何操作影响这一存储单元的所有对象,代码如下:
调用:
然后,内存中数据如图3-4所示。
因为静态成员的共享性,所以就必须考虑其数据安全。除了前面讲到的线程安全以外,还必须考虑对象对其进行访问时的安全性。为此,需注意以下几点:
1)静态成员的初始化操作先于对象的实例化进行,所以在它们的初始化中不要启动线程,以免造成数据访问的问题。同时,静态成员的初始化操作中也不应该有依赖关系。
图3-4 内存数据分配状态
2)不用静态变量保存某个对象的状态,而应该保存所有对象应该共有的状态。
3)不用对象来访问静态变量,而用类名来访问静态变量。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。