您的位置:首页 > 编程语言 > Java开发

Java内存区域及管理

2016-01-08 17:28 344 查看
概述:运行时数据区



数据区域详解

程序计数器

程序计数器,可以看做是当前线程执行代码的行号指示器,字节码解释器通过改变程序计数器的值来选择下一条执行的语句。分支,循环,跳转,异常处理,线程恢复都是通过这个计数器来完成的。

在多线程中,每个线程都有自己的计数器,这个区域是线程私有的。

当执行Native方法时,这个计数器值为空。

这个区域是唯一没有OutOfMemeryException的内存区域。

虚拟机栈

虚拟机栈也是线程私有的,生命周期和线程相同。

虚拟机栈主要用来描述方法执行的内存模型:每个方法执行时都会在在虚拟机栈中创建一个栈帧,在这个栈帧中存储方法中的局部变量,操作数栈,动态链表,方法出口信息

方法从调用到执行完成的过程就对应着一个栈帧入栈道出栈的过程。

如果无法在虚拟机栈申请到足够的内存空间,就会抛出stackOverflowError异常。

本地方法栈

本地方法栈与虚拟机的作用相似,不同之处在于虚拟机栈为虚拟机执行的Java方法服务,而本地方法栈则为虚拟机使用到的Native方法服务。有的虚拟机直接把本地方法栈和虚拟机栈合二为一。

会抛出stackOverflowError和OutOfMemoryError异常。

方法区

方法区是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载

当方法区无法满足内存分配需要时,将抛出OutOfMemoryError异常。

运行时常量池

是方法区的一部分,它用于存放编译期生成的各种字面量和符号引用。比较常见的是字符串常量池

堆是所有线程共享的一块内存区域,在虚拟机启动时创建,所有的对象实例都存放在此内存区域。

堆是垃圾收集器管理的主要区域。由于现在收集器基本采用分代回收算法,所以Java堆还可细分为:新生代和老年代。从内存分配的角度来看,线程共享的Java堆中可能划分出多个线程私有的分配缓冲区(TLAB)。

Java堆可以处于物理上不连续的内存空间,只要逻辑上连续的即可。

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

对象的创建



创建一个对象通常是需要new关键字

当虚拟机遇到一条new指令时,首先检查这个指令的参数是否在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。

如果没有那么执行相应的类加载过程。类加载检查通过后,虚拟机将为新生对象分配内存。分配的方式有两种:

3.1 一种叫 指针碰撞 ,假设Java堆中内存是绝对规整的,用过的和空闲的内存各在一边,中间放着一个指针作为分界点的指示器,分配内存就是把那个指针向空闲空间的那边挪动一段与对象大小相等的距离。

3.2 另一种叫 空闲列表 :如果Java堆中的内存不是规整的,虚拟机就需要维护一个列表,记录哪个内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录。

另 外一个需要考虑的问题就是对象创建时的线程安全问题,有两种解决方案:

4.1 一是对分配内存空间的动作进行同步处理;

4.2 另一种是吧内存分配的动作按照线程划分在不 同的空间之中进行,即每个线程在Java堆中预先分配一小块内存(TLAB),哪个线程要分配内存就在哪个线程的TLAB上分配,只有TLAB用完并分配 新的TLAB时才需要同步锁定。

对象的访问定位

主要的访问方式有使用句柄和直接指针两种:

句柄:


Java堆将会划出一块内存来作为句柄池,引用中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息 。如图所示:





直接指针:


Java堆对象的布局要考虑如何放置访问类型数据的相关信息,引用中存储的就是对象地址 。如图所示:





来自为知笔记(Wiz)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: