首页 > 代码库 > 7.4.3 堆上的对象
7.4.3 堆上的对象
Java的存储管理模式中,堆分配(heap-based memoryallocation,dynamic memory allocation)是Java程序员需要大致了解的。7.4.3的学习要点:
对象堆上分配的含义;
现代JVM,引用作为直接指针实现(速度和碎片整理)
1.Java对象的内存总是在heap中分配
在Java语言层面,任何Java对象所需的空间都在Java堆上动态分配,遵循堆分配的存储管理模式。
涉及两个问题:
Java不在栈中保存对象的数据的好处。对比C++,程序员不需要知道对象的大小(虽然通过某些技术可以获得类似C/C++的sizeof()操作);不需要区分stack中和heap中的对象(C++语言中对象空间的分配,将形成两种对象模型,以及子类的切割问题)。
抽象和实现。不在栈中分配对象空间仅仅是Java语言层面的结论。准确说,仅具有《Java虚拟机规范》中规定的抽象的、逻辑上的含义。Java程序员从来不需要花费时间和精力去考虑能否在栈中分配对象空间。但是,为了提供尽可能好的性能,JVM的实现者则可以使用栈分配模式保存“适当的”对象。例如当它采用逃逸分析(Escape analysis)技术判定一个特定对象的整个生命周期不会超过/逃逸出一个给定的JVM栈帧的生命周期,那么该对象就能够安全地进行栈分配。
必须充分认识到抽象和实现分离的意义——JVM的使用者不必关心底层的、与性能相关的细节,而JVM的实现者又能够提供尽可能好的性能。
2.对象的内部表示
“PL6. 面向对象程序设计·Internal representations ofobjects and method tables 对象和方法表的内部表示”的知识单元,在Java语言的教学中远不如C++中那么值得详尽介绍。
要点:
对象所需的空间保存它所有的(包括所有祖先类定义的)实例变量的值,以及系统开销(overhead)。因为通过对象的引用,JVM除了需要定位对象的实例变量的值,还必须能够进行相关的操作。系统开销包括:访问它在方法区中的类型数据,例如在造型为父类时、执行instanceof操作时等。此外,Java对象还需要保存与线程相关的数据——锁,JVM中的每一个线程都有一个锁对象;与垃圾回收相关的数据,随着垃圾回收算法的不同,而被附加不同的数据。
所以,《Java虚拟机规范》中没有规定对象的内部表示。对象的内部表示有JVM实现者决定,它关系着整个堆以及垃圾回收器的设计。
但是,现代JVM的引用都是作为指针实现的。换言之,在JVM看来,引用就是指针;(早期的JVM实现中(如Classic VM),引用指向保存若干指针的结构,称为句柄——相关的内容和图,略)。
在Java程序员看来,引用是安全指针——没有算术操作的功能。
图 7?7 引用作为直接指针实现
更多的时候,我们画对象的空间时会将系统开销省略掉。而在《编程导论(Java)》中,从[2.4.1 引用的涵义]开始就一直将系统开销画从来。
(图的“类型数据”后面应该加个“等”)
随便提醒:图7-7中,引用变量Xxx@...它的位置可以是方法区(method area)、Java堆和Java栈。
7.4.3 堆上的对象