Java内存区域与内存溢出异常

来源:互联网 发布:js input不可编辑属性 编辑:程序博客网 时间:2024/05/21 14:43

Java内存区域与内存溢出异常

运行时数据区域

java内存空间

程序计数器

java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间程序计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。

Java虚拟机栈

Java虚拟机栈也是私有的,他的生命周期和线程相同。虚拟机栈描述的是java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧用于储存局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应这一个栈帧在虚拟机占中入栈到出栈的过程。

局部变量表所存放了编译器克制的各种基本数据类型(boolean,byte,char,short,int,float,long,double)、对象引用(reference类型,他不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是执行一个代表对象的句柄或其他榆次对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址)。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量是完全确定的,在方法运行期间不会改变局部变量表的大小。

本地方法栈

虚拟机栈为虚拟机执行java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native提供方法服务。

java堆

java堆是Java虚拟机中所管理的内存中最大的一块。被所有线程所共享,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。很多时候也被称为GC堆。由于现在收集器基本都是采用的分带收集算法。所以java堆中可以分为:新生代和老年代。再细致一点的有Eden空间、From Survivor空间、To Survivor空间等。java堆可以处于物理上不连续的内存空间上,只要逻辑上是连续的即可,就像我们的磁盘空间一样。

方法区

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

运行时常量池

运行时常量池是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息的常量池,用于放编译器生成的各种字面量和符号引用,这部分将在类加载后进入方法去的运行时常量池存放。

直接内存

直接内存不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域.

HotSpot虚拟机对象探秘

对象的创建

堆内存分配使用两种方式:

方法名称 收集器 指针碰撞(堆内存规整) serial,parNew等待Compact过程的收集器 空闲列表(不规整) 基于Mark-Sweep算法的CMS收集器

两种内存分配冲突的解决方法:

采用CAS配上失败重试的方式保证更新操作的原则性

把内存分配的动作按照线程划分在不同的空间中进行,即在每个线程中预先分配一块内存,成为本地线程分配缓冲。

对象储存布局

对象在内存中储存的布局可以分为三块区域:对象头,实例数据和对齐填充。

对象头:(1)运行时自身的数据,如哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。(2)类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。如果对象是数组在对象头中应该还保存一个数组长度的数据。

实例数据就是在程序代码中定义的各种类型的字段内容

对齐填充不是必然存在的,只有当实例数据不是8字节的倍数,没有对齐的时候,才需要对齐填充补全

对象的访问定位

使用对象的时候通过栈上的reference类型数据来操作堆上的具体对象。

如何定位取决于虚拟机的实现:

(1)句柄定位:在java堆中划分出一块内存来作为句柄池,reference中储存的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体的地址信息。

(2)直接指针访问:Java堆对象的布局中就必须考虑如何放置访问类型数据的相关信息。二reference中储存的直接就是对象地址。

原创粉丝点击