当程序运行时,能够可视化其内容的排布方式是十分有帮助的,对于内存管理来说尤其如此。下面列举了5种数据存储方式。
1.寄存器 (register)。这是速度最快的数据存储方式,因为它保存数据的位置不同于其他方式:数据会直接保存在 中央处理器 (central processing unit,CPU)里 。然而寄存器的数量是有限的,所以只能按需分配。此外,你不能直接控制寄存器的分配,甚至你在程序中都找不到寄存器存在过的证据(C和C++是例外,它们允许你向编译器申请分配寄存器)。
2.栈 (stack)。数据存储在随机存取存储器(random-access memory,RAM)里,处理器可以通过 栈指针 (stack pointer)直接操作该数据。具体来说,栈指针向下移动将申请一块新的内存,向上移动则会释放这块内存。这是一种极其迅速和高效的内存分配方式,其效率仅次于寄存器。只不过Java系统在创建应用程序时就必须明确栈上所有对象的生命周期。这种限制约束了程序的灵活性,因此虽然有一些数据会保存在栈上(尤其是对象引用),对象本身却并非如此。
3.堆 (heap)。这是一个通用的内存池(使用的也是RAM空间),用于存放所有Java对象。与栈不同的是,编译器并不关心位于堆上的对象需要存在多久。因此,堆的使用是非常灵活的。比如,当你需要一个对象时,可以随时使用new来创建这个对象,那么当这段代码被执行时,Java会在堆上为该对象分配内存空间。然而这种灵活性是有代价的:分配和清理堆存储要比栈存储花费更多的时间(如果你可以像C++那样在栈上创建对象的话)。好消息是,随着时间的推移,Java的堆内存分配机制已经变得非常高效了,所以你并不需要太过关注此类问题。
4.常量存储 (constant storage)。常量通常会直接保存在程序代码中,因为它们的值不会改变,所以这样做是安全的。有时候常量会与其他代码隔离开来,于是在某些嵌入系统里,这些常量就可以保存在只读存储器(read-only memory,ROM)中 。
5.非RAM存储 (non-RAM storage)。如果一段数据没有保存在应用程序里,那么该数据的生命周期既不依赖于应用程序是否运行,也不受应用程序的管制。其中最典型的例子之一是“序列化对象”(serialized object),它指的是转换为字节流(叫作“序列化”)并可以发送至其他机器的对象。另一个例子则是“持久化对象”(persistent object),它指的是保存在磁盘上的对象,而这些对象即便在程序结束运行之后也依然能够保持其状态。这些数据存储类型的特点在于,它们会将对象转换成其他形式以保存于其他媒介中,然后在需要的时候重新转换回常规的RAM对象。Java支持轻量级的持久化对象存储,而JDBC以及Hibernate 等库则提供了更为成熟的解决方案,即支持使用数据库存取对象信息。