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

Java基础知识---内存区域与内存溢出异常

2017-09-25 18:48 344 查看

Java基础知识---内存区域与内存溢出异常

今天记录的是我学习java内存基础知识的知识点,废话不多说直接上干货。
一.Java运行时的内存区域结构究竟是怎么样的呢?
下面这张是在《深入了解Java虚拟机》中描述内存区域结构图



根据上图,我们逐一了解java虚拟机内存的各个模块:

1.程序计数器(program counter register):
是一个只占用了一块比较小的内存空间的模块。可以看作是当前线程所执行的字节码文件(class)的行号指示器。在虚拟机的世界中,字节码解释器就是通过改变计数器的值来选取下一条执行的字节码指令,分支、循环、跳转、异常处理、线程恢复都需要这玩意来实现的因为处理器在一个确定是时刻只会执行一个线程中的指令,线程切换后,是通过计数器来记录执行痕迹的,因而可以看出,程序计数器是每个线程私有的。如果执行的是java方法,那么记录的是正在执行的虚拟机字节码指令的地址的地址,如果是native方法,计数器的值为空(undefined)。这个内存区域是唯一一个在java虚拟界规范中没有规定任何OutOfMemoryError的情况的区域。
2.虚拟机栈(Java Virtual Machine Stack):
虚拟机栈描述的是java方法执行的内存模型,是线程私有的,当每一个方法在执行的时候就会创建一个栈帧(存储着局部变量,操作数栈,动态链接,方法出口第信息),一个方法的调用直至完成的过程就是这个方法的栈帧入栈到出栈的过程。
栈帧主要包含三个部分:局部变量表,操作数栈,帧数据区。

2.1.栈帧的定义(stack frame):

栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈的栈元素。栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息。

     每一个方法从调用开始到执行完成的过程,就对应着一个栈帧在虚拟机栈里面从入栈到出栈的过程。
     对于执行引擎来说,活动线程中,只有栈顶的栈帧是有效的,称为当前栈帧,这个栈帧所关联的方法称为当前方法。执行引擎所运行的所有字节码指令都只针对当前栈帧进行操作。

 



 

2.1.1栈帧的组成
 (1)局部变量表
      局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。在Java程序被编译成Class文件时,就在方法的Code属性的max_locals数据项中确定了该方法所需要分配的

      最大局部变量表的容量。

      局部变量表的容量以变量槽(Slot)为最小单位,32位虚拟机中一个Slot可以存放一个32位以内的数据类型(boolean、byte、char、short、int、float、reference和returnAddress八种)。

      reference类型虚拟机规范没有明确说明它的长度,但一般来说,虚拟机实现至少都应当能从此引用中直接或者间接地查找到对象在Java堆中的起始地址索引和方法区中的对象类型数据。

      returnAddress类型是为字节码指令jsr、jsr_w和ret服务的,它指向了一条字节码指令的地址。

      虚拟机是使用局部变量表完成参数值到参数变量列表的传递过程的,如果是实例方法(非static),那么局部变量表的第0位索引的Slot默认是用于传递方法所属对象实例的引用,在方法中通过this访问。

      Slot是可以重用的,当Slot中的变量超出了作用域,那么下一次分配Slot的时候,将会覆盖原来的数据。Slot对对象的引用会影响GC(要是被引用,将不会被回收)。

  系统不会为局部变量赋予初始值(实例变量和类变量都会被赋予初始值)。也就是说不存在类变量那样的准备阶段。

 (2)操作数栈
      Java虚拟机的解释执行引擎被称为"基于栈的执行引擎",其中所指的栈就是指-操作数栈。

      操作数栈也常被称为操作栈。

     和局部变量区一样,操作数栈也是被组织成一个以字长为单位的数组。但是和前者不同的是,它不是通过索引来访问,而是通过标准的栈操作—压栈和出栈—来访问的。比如,如果某个指令把一个值压入到操作数栈中,稍后另一个指令就可以弹出这个值来使用。

      虚拟机在操作数栈中存储数据的方式和在局部变量区中是一样的:如int、long、float、double、reference和returnType的存储。对于byte、short以及char类型的值在压入到操作数栈之前,也会被转换为int。

      虚拟机把操作数栈作为它的工作区——大多数指令都要从这里弹出数据,执行运算,然后把结果压回操作数栈。比如,iadd指令就要从操作数栈中弹出两个整数,执行加法运算,其结果又压回到操作数栈中,看看下面的示例,它演示了虚拟机是如何把两个int类型的局部变量相加,再把结果保存到第三个局部变量的:

本地方法栈:和虚拟机栈类似,不过执行的是本地方法



在栈这个内存模型中定义了两种异常,一个是OutOfMemoryError(创建新对象的速度太快,以致于垃圾回收来不及,没有空间new对象,可以配置参数解决),和StackOverflowError(调用函数层级太多,典型的入死递归)

3.堆 

有句话是这样的:当你创建一个对象时,堆用来保存对象的数据,栈用来保存对该对象的引用。

堆是java内存管理中最大的模块,是被所以的线程共享的,辞区域的唯一目的就是存放对象的实力,几乎所以的对象都是在堆上创建的,(JIT编译器,逃逸分析技术),是垃圾回收的主要区域,由于目前的垃圾回收机制主要是分代回收,所以堆又分为新生代和老年代,如果堆中没有空间完成实例分配,并且也无法扩展时会报OutOfMemoryError。关于堆这一块主要在垃圾回收是讲
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: