1. 什么是JVM运行时数据区
2. 解析
2.1 方法区(Method Area)
(1)方法区是所有线程共享的内存区域,它用于存储已被Java虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码(JIT代码)等数据
类型信息:
①这个类型的完整有效名称(包名.类名)
②这个类型的直接父类的完整有效名称(interface和object没有父类)
③这个类型的修饰符(public、abstract、final的子集)
④这个类型的直接接口的一个有序列表
(2)它有个别命叫Non-Heap(非堆)。当方法区无法满足内存分配需求时(比如定义了太多的类、加载的第三方类太多),抛出OutOfMemoryError异常。
2.2 堆(Heap)
内存中的堆和数据结构中的堆毫无关系(硬要扯上关系,只能说,都是一堆杂乱无章的东西),不像下面说的虚拟机栈,多多少少跟数据结构沾点边。
(1)java堆是java虚拟机所管理的内存中最大的一块,是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例。
(2)在Java虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配。
(3)java堆是垃圾收集器管理的主要区域,因此也被成为“GC堆”。
(4)从内存回收角度来看java堆可分为:新生代和老生代。
(5)从内存分配的角度看,线程共享的Java堆中可能划分出多个线程私有的分配缓冲区。
(6)无论怎么划分,都与存放内容无关,无论哪个区域,存储的都是对象实例,进一步的划分都是为了更好的回收内存,或者更快的分配内存。
(7)根据Java虚拟机规范的规定,java堆可以处于物理上不连续的内存空间中。当前主流的虚拟机都是可扩展的(通过 -Xmx 和 -Xms 控制)。如果堆中没有内存可以完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。
2.3 程序计数器(Program Counter Register)
(1)在java中最小的执行单位是线程,程序计数器是一块较小的内存空间,它可以看作是:保存当前线程所正在执行的字节码指令的地址(行号)
(2)由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,一个处理器都只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都有一个独立的程序计数器,各个线程之间计数器互不影响,独立存储。称之为“线程私有”的内存。程序计数器内存区域是虚拟机中唯一没有规定OutOfMemoryError情况的区域。
2.4 虚拟机栈(Java Virtual Machine Stacks)
栈在数据结构中的概念就是一个先进后出的结构,其实在内存中也是一样的,但是内存中的栈所存储的内容是什么呢?就是方法。试想java中的方法一层一层的往下调用,什么时候结束,结束后回到什么状态都是内存需要考虑的问题,于是有了内存中的栈,栈的元素就是对应方法的栈帧,即一个方法一个栈帧,调用时也满足先进后出。
那方法中我们可视的就是形参、局部变量等,而且一旦方法结束调用,就会超出这些变量的作用域,此时,栈帧也会释放,所以这些形参、局部变量就会存储在栈帧->栈中。那不是new出来的变量都会存储在堆中吗?包括方法中定义的局部变量也可以new出来啊?没问题的,栈帧中存储的只是变量的地址,并不是变量本身,当需要获取变量内容时,就会从栈帧中取变量的地址,然后去寻找变量本身。
所以按照调用顺序,方法1->方法2->。。。。这样的一个结构(就像自己读代码时一层一层往里看一样),只有在一个线程下才可以实现(读代码时也是单线程,没办法跳出阅读其他代码),所以栈不是线程共享的
stack overflow即栈溢出,栈是按照方法的调用顺序,存储一个个方法对应的栈帧的,什么时候才能溢出呢?正常一个方法能占用多少内存吗?加一起也不会多大呀,所以不太可能因为方法中定义很多的局部变量导致栈溢出。只有当栈帧一直在增长,才会逐渐把栈填满,那就是递归调用一个方法呗,否则正常的方法调用总是有限次的。所以当递归的退出条件没写好时,会发生StackOverflow
(1)java虚拟机是线程私有的,它的生命周期和线程相同。
(2)虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
(3)空间不够时,抛出StackOverflowError
解释:每虚拟机栈中是有单位的,单位就是栈帧,一个方法一个栈帧。一个栈帧中他又要存储,局部变量,操作数栈,动态链接,出口等。
解析栈帧:
(1)局部变量表:是用来存储我们临时8个基本数据类型、对象引用地址、returnAddress类型。(returnAddress中保存的是return后要执行的字节码的指令地址。)
(2)操作数栈:操作数栈就是用来操作的,例如代码中有个 i = 6*6,他在一开始的时候就会进行操作,读取我们的代码,进行计算后再放入局部变量表中去
(3)动态链接:假如我方法中,有个 service.add()方法,要链接到别的方法中去,这就是动态链接,存储链接的地方。
(4)出口:出口是什呢,出口正常的话就是return 不正常的话就是抛出异常落
无论是普通调用方法还是递归调用方法,都会创建一个个栈帧
栈指向堆是什么意思?
栈指向堆是什么意思,就是栈中要使用成员变量怎么办,栈中不会存储成员变量,只会存储一个应用地址,堆中的数据等下讲
2.5 本地方法栈(Native Method Stack)
(1)本地方法栈很好理解,他很栈很像,只不过方法上带了 native 关键字的栈字
(2)它是虚拟机栈为虚拟机执行Java方法(也就是字节码)的服务
(3)native关键字的方法是看不到的,必须要去oracle官网去下载才可以看的到,而且native关键字修饰的大部分源码都是C和C++的代码。
(4)同理可得,本地方法栈中就是C和C++的代码
(5)空间不够时,抛出StackOverflowError