JVM-运行时数据区域(2)

来源:互联网 发布:淘宝魔切页面 编辑:程序博客网 时间:2024/06/05 15:02

JVM-运行时数据区域(2)

线程共享:

(1)java堆:

java堆是java虚拟机所管理内存中最大的一块,java堆是所有线程共享的一块内存区域。

 java堆在虚拟机启动的时候就创建了(java虚拟机桟的桟帧中的局部变量表在编译时期就分好了内存)。java堆的是用来存放对象实例的。所有的对象实例和数组都在java堆中分布。

 java堆是垃圾收集器管理的主要区域。java堆可以细分为:老年代新生代;再细致一点的划分可以是Eden空间,From Survivor空间,To Survivor空间

从内存分配的角度来看,线程共享的java堆中可能划分出多个线程私有的分配缓冲区(TLAB,该区域给每个对象 分配内存时使用,后面会有介绍)

java堆在物理上可以处于不连续的内存空间中,只要逻辑上连续就可以,就像我们的磁盘空间,可以通过-Xmx-Xms来控制 扩展java堆。

如果在堆中没有内存完成实例分配,并且堆也无法扩展的时候,会抛出OutOfMemoryError异常


(2)方法区:

方法区与java堆一样,是一个线程共享的区域,它用于存储已经被虚拟机加载的类,常量,静态变量,即使编译器编译后的代码等数据

在虚拟机规范中,方法区是java堆的一个逻辑部分,但是实际上方法区并不是堆。

很多人将方法区称为永久代,是因为在HotSpot虚拟机中将GC的分代收集器扩展至方法区,确切的说是用永久代来实现方法区。原则上怎么实现方法区属于虚拟机的细节,不受虚拟机控制,但是使用永久代实现方法区并不是一个好的主意,因为永久代更容易遇到内存溢出的问题,在HotSpot虚拟机的后续发展上,已经准备放弃永久代而改为Native Memory来实现方法区的规划,jdk1.7将常量池移出方法区。

Java虚拟机规范对方法区的限制非常宽松,其和java堆一致,同样不需要连续的内存来存储,并且可以选择固定大小或者扩展内存,此外,还可以选择不实现垃圾收集

垃圾收集器在方法区中出现爱你的较少,但是并不代表存在方法区中的数据不会被回收,这个区域的内存回收目标主要针对的是常量池的回收和对类型的卸载,尤其是对类型卸载的回收是相当的苛刻,回收成绩难以令人满意,但是方法区的内存回收是必须的。

当方法区无法满足内存的分配的需求的时候,有时候会抛出OutOfMemoryError异常。

(3)运行时常量池:

运行时常量池是方法区的一部分,所以也是线程共有

首先,先进行声明运行时常量池和常量池并不是一回事,常量池在class文件中存储,在类被加载后,常量池会进入方法的运行时常量池。class文件的常量池中主要存放各种字面量和符号引用。

字面量:就是常量,字符串等
符号引用:指的就是用一组符号来描述引用的目标(比如user类中引用Person类,则用person这个字符串来表示Person类所在的地址)
直接引用:直接指向目标的指针。

java虚拟机对class文件的每一个部分的格式都有严格的规定,但对于运行时常量池,java虚拟机没有任何规定细节。

运行时常量池相比较class文件常量池的区别是,运行时常量池具备动态性,也就是说java语言并不是要求常量只能在编译时期产生,也就是说不只是编译后的class文件的常量池可以进入运行时常量池,运行期间可以将新的常量放入运行时常量池,如String类的intern()方法。

运行时常量池作为方法区的一部分,当常量池无法得到足够的内存时,报OutOfMemoryError。

直接内存

运行时内存不是虚拟机运行时数据的一部分,但是使用频繁,且也会抛出OutOfMemoryError异常。

在jdk1.4之后新加入了一个NIO类,引入了一种基于通道和缓冲区IO方式,它可以使用Native函数库直接分配堆外内存,并且在将指向该内存的引用存储在java的堆中,这样提高了性能,避免了在java堆和Native堆中来回复制数据。

本地内存虽然不会收到java堆大小的限制,但是会受到计算机的内存限制,因此在无法申请到足够的直接内存空间时,会抛出OutOfMemoryError异常。

原创粉丝点击