内存管理机制-内存模型与对象的访问

 

RoadMap

 

虚拟机中的逻辑结构

虚拟机中的内存结构根据虚拟机规范划分出了五个经常使用区域,这些区域各自有各自的用途,有些随用户线程而存在,有些则随虚拟机进程而存在。 
五个区域分别为,虚拟机栈,本地方法栈,程序计数器,堆,方法区java

 

程序计数器 (Program Counter Register, PCR)

程序计数器是随线程而生的,它是线程独立的,用来记录当前线程的字节码与指令,保证下一次线程唤醒以后,能回到正确的执行位置,说白了就是线程唤醒能继续执行。 
程序计数器是一块很是小的内存区域,也是惟一一个没有规定任何OutOfMemoryError的区域算法

虚拟机栈 (Virtual Machine Stack, VM Stack)

虚拟机栈也是线程私有的,它描述了Java方法执行的内存模型:每一个方法被执行是都会建立一个帧栈(Stack Frame) 用于存储 局部变量表 操做数栈 动态连接 方法出口。每个方法被调用直至执行完成的过程,就是对应着一个帧栈在虚拟机栈中从入栈到出栈的过程。 
局部变量表存放了编译期可知的基本类型和引用类型。 
在这个区域中,虚拟机规范规定了两种异常状况,StackOverflowError:当线程请求栈的深度大于虚拟机容许的深度,将会抛出此异常。 OutOfMemoryErro:当虚拟机栈在动态扩展长度时,没法申请到足够内存时将会抛出此异常数组

本地方法栈 (Native Method Stack, NM Stack)

本地方法栈与虚拟机栈发挥的做用很是相似,主要是为本地方法服务,虚拟机栈是为Java 方法服务的。 有些虚拟机实现 如 HotSpot 已经将NM Stack 和VM Stack 合二为一了函数

 

堆 (Heap)

heap 是java 虚拟机管理的内存中最大的一块,也是线程共享的。在虚拟机启动时 自动建立。 
除了JIT编译和逃逸分析技术下的对象,绝大多数的对象实例和数组都在堆上分配。 
同时 堆也是 垃圾收集器(Garbage Collection)主要工做的区域。 
若是堆中没有可分配的内存空间会抛出OutOfMemoryError性能

方法区

方法区与堆同样 是线程共享的,用于存储常量,静态变量,加载的类信息,即时编译的代码数据块。虽然说 虚拟机规范把方法区化为堆的一部分,它却又一个别名non-heap,有意把它和堆区分开了,有些地方也把这地方称为,永久代,主要是由于把GC收集扩展至这个区域了或者说用永久代来实现方法区的功能。 
当方法去没法知足内存分配需求时,将会抛出OutOfMemoryError 
方法去主要存有: 
*类信息 
*类变量 
*类型常量池 
*方法信息 
String常量池即是其中之一。ui

直接内存

直接内存(Direct Memory)并非虚拟机运行时数据区的一部分,也不是Java 
虚拟机规范中定义的内存区域,可是这部份内存也被频繁地使用,并且也可能致使 
OutOfMemoryError 异常出现,因此咱们放到这里一块儿讲解。 
在JDK 1.4 中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel) 
与缓冲区(Buffer)的I/O 方式,它可使用Native 函数库直接分配堆外内存,然 
后经过一个存储在Java 堆里面的DirectByteBuffer 对象做为这块内存的引用进行 
操做。这样能在一些场景中显著提升性能,由于避免了在Java 堆和Native 堆中来 
回复制数据。spa

堆中的分代划分

heap 中内存划分基于分代思想 主要分为 新生代,老年代和存活区。 线程

新生代 Eden

大多数状况下,对象在新生代中分配。当Eden区没有足够的空间的时候会发起一次MinorGC指针

老年代 Old Generation

大对象直接进入老年代,所谓大对象就是指须要大量连续内存空间的Java 对象,如数组,字符串等。 
同时基于分代思想,虚拟机给每一个对象定义了一个对象年龄。当对象在Eden区通过MinorGC后还生存下来后,会被移动到Suivior Space中并记为年龄为1,而且在Suivior Space 每通过一次GC后 对其中存活的对象年龄加1,当对象年龄超过必定的阈值(默认为15)时,将会被移动到老年代。对象

存活区 Suivivor Space

存活区有点像青年代,用于那些新生代存活下来而未到达老年代的对象。 
同时 存活区分为From,to 两块空间相同的区域,做为复制回收算法的区域

若是只是按照对象年龄阀值来判断是否须要移动到老年代,难以应付多变的内存状况,加入在存活区有着很是大量的年轻对象(2,3,4,5,6,7岁等)以致于 存活区内存不太够用的状况。对于这种状况,survivor有一个动态年龄断定。在存活区中的相同年龄的全部对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就直接进入老年代。

对象访问方式

Object obj = new Object() 
Object obj 声明了一个引用对象 
new Object() 在堆中开辟了空间并生成对象 
所谓对象的访问方式是指,引用类型obj如何定位到堆中的具体对象Object()的。

虚拟机中主流的方式有两种直接指针和句柄池

句柄池

若是使用句柄访问方式,堆中会划分出一小块内存做为句柄池,reference存储的是对象的句柄地址,而句柄中包含了对象实例数据和各自数据的具体地址信息。优势在于,reference存储的是稳定的句柄地址,在对象被移动时,只会改变句柄中的实例数据指针,reference自己不须要改变。 

直接指针

若是使用饿了直接指针访问,reference中直接存储了对象的地址。次方式的优势在于节约对象访问方式,对于频繁的访问对象,直接指针节约了一个层级的访问时间。 

相关文章
相关标签/搜索