深入java虚拟机第5章, 堆

来源:百度文库 编辑:神马文学网 时间:2024/03/29 07:10:24

深入java虚拟机第5章, 堆

java程序在运行时所创建的所有类实例或数组都存放在同一个堆中。而每个java虚拟机实例中只存在一个堆空间,因此所有线程都将共享这个堆,又由于每个java程序独占一个java虚拟机实例,因此都有它自己的堆空间。

因此要考虑多线程访问对象(堆数据)的同步的问题了。

java虚拟机有一条在堆中分配新对象的指令,但是没有释放内存的指令。java虚拟机的垃圾回收机制负责回收没有被使用的内存。

只要有一个对象引用,虚拟机就必须能够快速定位对象类型的数据。另外它也必须通过该对象引用访问相应的类数据(存储于方法区中的类型信息)。因此在对象中通常会有一个指向方法区的指针。

堆空间的设计举例

一种设计:把堆分为两部分:一个句柄池,一个对象池。句柄池的每个条目有两个部分,一个指向对象实例的指针,一个指向方法区类型数据的指针。这种设计的好处是便于堆碎片的整理。每次移动对象池中的对象,只需要更改指向对象的新地址就可以了,缺点是每次访问对象的实例变量都要经过两次指针的传递。

 

另一种设计:对象指针直接指向一组数据,该数据包括对象实例数据和指向方法区的指针。但是使得当整理内存碎片而移动对象变得复杂。

 

java虚拟机使用一种特殊的数据结构把方法区和对象引用联系起来。这个特殊的数据结构位于方法区:由一个指向方法区对应类数据的指针和此对象的方法表。

方法表是一个指针数组,其中每一项都是之歌指向“实例方法数据”的指针,实例方法可以被那类的对象调用。

实例方法数据包含以下信息:

1)此方法的操作数栈和局部变量区的大小

2)此方法的字节码

3)异常表

 

每个对象都有一个对象锁,用来在多个线程访问这个对象时进行同步。这个锁是可重入锁。一个线程可以追加请求并获得这个锁,但请求几次,也要释放几次。

 

每个对象逻辑上还有与实现等待集合(wait set)的数据相关联。锁是用来实现多个线程对共享数据的互斥访问的,而等待集合是为了多个线程为了实现同一个共同目标而协调工作的。

 

等待集合由等待方法和通知方法联合使用,每个类都从objec继承了三个等待方法,wai()的重载方法和两个通知方法notify和notifyall。

当一个线程调用等待方法时,java虚拟机就阻塞这个线程,并将这个线程放入这个对象的等待集合,直到另外一个线程在这个对象上调用通知方法,这个线程中的一个或者多个线程才会被唤醒。

 

方法区的结构

方法表,方法数据,类中所有数据的入口点。

 

最后一种数据是堆中对象的垃圾收集管理数据。垃圾收集器必须使用某种方式追踪程序引用的每个对象。

另外在java中数组是真正的对象,因此也存放在堆中。

 

程序计数器

每个java线程都有自己的pc寄存器,当线程启动时创建。pc寄存器的大小是一个字长,即能维持一个本地指针,也能持有一个returnAddress。当线程执行某个方法时,pc寄存器的内容总是下一个被执行指令的“地址”,这里的地址可以是一个指针,也可以是该方法起始指令的偏移量。如果该线程在执行一个本地方法,这个寄存器的值为undefined。