概述
运行时数据区域
java虚拟机在运行时有包含一下几个运行时的数据区域:
- 方法区
- 堆
- 虚拟机栈
- 本地方法栈
- 程序计数器
程序计数器
程序计数器是一块较小的内存空间,可以看作当前线程所执行的字节码的行号指示器。
由于java的多线程是由线程的轮流切换并分配处理器执行时间的方式实现,在任何时刻只有一条线程命令在执行。
如果线程执行的java方法,计数器记录的是正在执行的虚拟机字节码指令的地址,如果执行的是native方法,计数器则为空(Undefinded),此内存区域是唯一一个在java虚拟机规范中没有规定任何OutMemoryError情况的区域。
[========]
java虚拟机栈
- java虚拟机栈也是线程私有的,它的生命周期与线程相同。
- java虚拟机栈描述的是java方法执行的内存模型:每个方法呗执行的时候都会同时创建一个栈帧,用于存储布局变量表、操作栈、动态链接、方法出口等信息。每个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
- 通常将java内存分为;堆内存(heap)和栈(stack,即虚拟机栈或虚拟机栈局部变量)局部变量表存放编译器可知的各种基本数据类型(boolean、byte、char、short、int、float、double)、对象引用(reference类型)、returnAddress类型
- 64位长度的long和double类型占据2个局部变量空间(Slot),其他数据类型占用1个
- 空间在编译时就完成分配,局部变量空间是确定的
java虚拟机规范,在此区域有两个异常:
- 如果线程请求栈 的深度大于虚拟机所允许的范围,抛StackOveflowError异常;
- 如果虚拟机栈可动态扩展(大部分虚拟机均可动态扩展)java虚拟机的规范中允许固定长度的虚拟机栈,如果扩展时无法申请足够的内存,就会抛出OutOfMemoryError异常
本地方法
本地栈与虚拟机栈区别:
- 虚拟机栈为虚拟机执行java方法服务,
- 本地栈为虚拟机使用的Native方法服务
- sun 的SpotHot直接将上述两种合二为一
java堆
- java堆是虚拟机中所管理的内存中最大的一块,
- 被线程共享,
- 在虚拟机启动时创建。
- 唯一目的是存放对象实例
- 是垃圾收集器的管理的主要区域
- 收集器都采用分代收集算法:新生代和老年代,细分为(Eden空间、From Survivor空间、To Survivor空间)
- 主流虚拟机按照可扩展来实现(-Xmx和 -Xms ),如果在堆中没有内存可分配,并且堆无法再扩展,将抛出OutOfMemoryError
方法区(别名Non-Heap非堆)
- 线程共享内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量。及时编译器编译后的代码。
- HotSpot 虚拟机,方法区称为永久代(Permanent Generation),HotSpot中将垃圾回收器扩展至方法区
问题:
更容易遇到内存溢出问题(永久代有-X X:MaxPermSzie的上限),JDK1.7中已将永久代的字符串常量池移出。
运行时常量池(Runtime Constant Pool)
jdk1.7 之后将常量池放入堆中
- class 文件中有一项是常量池,用于存放编译器生成的各种字面量、和符号引用。
- 运行时常量池相对于Class文件常量池,重要特征 是具有动态性,也就是并非预置入class文件中常量池的内容才能进入方法区运行是常量池,此种方法运用较多的是String的intern()方法
直接内存
直接内存不是虚拟机运行时数据区的一部分,也不是java虚拟规范中定义的内存区域。,但是此部分也肯能导致OutOfMemoryError异常的出现
- jdk1.4中加入NIO类,基于通道与缓冲区的I/O方式,他可以使用Native函数库直接分配堆外内存,然后通过一个java堆中的DirectByteBuffer对象作为内存的引用进行操作。避免在java堆和native堆中来回复制数据。
- 本机直接的内存分配不会收java堆大小限制,但是书本机RAM限制。服务器管理员配置虚拟机参数时,会设置-Xmx参数,忽略直接内存,使得个内存区域大雨物理内存限制,从而动态扩展时出现OutOfMemoryError异常。
HotSpot虚拟机对象探秘
对象创建
创建对象: 虚拟机遇到new指令,检验这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析、和初始化过。如果没有,就必须执行相应的类加载过程。
对象内存分配:
- 指针碰撞,java堆中内存是规整的
- 空闲列表(Free List),java堆不是规整的
分配方式由java堆是否规整决定,java堆是否规整由采用的收集器是否带压缩整理功能决定
Serial、parNew 带Compact过程的收集器时,系统采用指针碰撞,使用CMS基于Mark-Sweep收集器时,通常采用空闲列表。
多线程问题: - 对分配内存空间的动作进行同步处理--实际虚拟机采用CAS配上失败从事的方式保证更新操作的原子性。
- 把内存分配的动作按照线程划分在不同的空间中进行,(本地线程分配缓冲),-XX:+/-UseTLAB 来配置是否使用TLAB参数。
对象的内存布局(待详补)
对象在内存中存储的布局可以分3个区域:对象头(Header)、实例数据(Instance Data)和对其填充(padding)。
- 对象头部分:第一部分用于存储对象自身的运行时数据,如哈希码、GC分代年龄、所状态标志、线程持有的锁、偏向线程Id、偏向时间戳,这些数据在32位和64位虚拟机中分别为32bit和64bit(mark word)。
网友评论