性能影响,对可用内存的搜索正是一个重要的原因。所以创建基于堆栈的对象要
快得多。
同样地,由于
C++
如此多的工作都在编译期间进行,所以必须考虑这方面的因素。
但在
Java
的某些地方,事情的发生却要显得“动态”得多,它会改变模型。创
建对象的时候,垃圾收集器的使用对于提高对象创建的速度产生了显著的影响。
从表面上看,这种说法似乎有些奇怪——存储空间的释放会对存储空间的分配造
成影响,但它正是
JVM
采取的重要手段之一,这意味着在
Java
中为堆对象分配
存储空间几乎能达到与
C++
中在堆栈里创建存储空间一样快的速度。
可将
C++
的堆(以及更慢的
Java
堆)想象成一个庭院,每个对象都拥有自己的
一块地皮。在以后的某个时间,这种“不动产”会被抛弃,而且必须再生。但在
某些
JVM
里,
Java
堆的工作方式却是颇有不同的。它更象一条传送带:每次分
配了一个新对象后,都会朝前移动。这意味着对象存储空间的分配可以达到非常
快的速度。“堆指针”简单地向前移至处女地,所以它与
C++
的堆栈分配方式几
乎是完全相同的(当然,在数据记录上会多花一些开销,但要比搜索存储空间快
多了)。
现在,大家可能注意到了堆事实并非一条传送带。如按那种方式对待它,最终就
要求进行大量的页交换(这对性能的发挥会产生巨大干扰),这样终究会用光内
存,出现内存分页错误。所以这儿必须采取一个技巧,那就是著名的“垃圾收集
器”。它在收集“垃圾”的同时,也负责压缩堆里的所有对象,将“堆指针”移
至尽可能靠近传送带开头的地方,远离发生(内存)分页错误的地点。垃圾收集
器会重新安排所有东西,使其成为一个高速、无限自由的堆模型,同时游刃有余
地分配存储空间。
为真正掌握它的工作原理,我们首先需要理解不同垃圾收集器(
GC
)采取的工
作方案。一种简单、但速度较慢的
GC
技术是引用计数。这意味着每个对象都包
含了一个引用计数器。每当一个句柄同一个对象连接起来时,引用计数器就会增
值。每当一个句柄超出自己的作用域,或者设为
null
时,引用计数就会减值。这
样一来,只要程序处于运行状态,就需要连续进行引用计数管理——尽管这种管
理本身的开销比较少。垃圾收集器会在整个对象列表中移动巡视,一旦它发现其
中一个引用计数成为
0
,就释放它占据的存储空间。但这样做也有一个缺点:若
对象相互之间进行循环引用,那么即使引用计数不是
0
,仍有可能属于应收掉的
“垃圾”。为了找出这种自引用的组,要求垃圾收集器进行大量额外的工作。引
用计数属于垃圾收集的一种类型,但它看起来并不适合在所有
JVM
方案中采用。
在速度更快的方案里,垃圾收集并不建立在引用计数的基础上。相反,它们基于
这样一个原理:所有非死锁的对象最终都肯定能回溯至一个句柄,该句柄要么存
在于堆栈中,要么存在于静态存储空间。这个回溯链可能经历了几层对象。所以,
如果从堆栈和静态存储区域开始,并经历所有句柄,就能找出所有活动的对象。
对于自己找到的每个句柄,都必须跟踪到它指向的那个对象,然后跟随那个对象
中的所有句柄,“跟踪追击”到它们指向的对象……等等,直到遍历了从堆栈或
静态存储区域中的句柄发起的整个链接网路为止。中途移经的每个对象都必须仍
评论