JVM是java的虚拟机,java虚拟机在执行java程序的时候会把管理的内存划分为若干个不同的数据区域
这些区域都有自己各自的用途,以及创建和销毁的时间,有的随着虚拟机进程的启动而存在,有的依赖于线程的启动和结束而建立和销毁,根据官方规范,java虚拟机所管理的内存会包括以下几个运行时的数据区域。
方法区(Method Area),虚拟机栈(VM stack),本地方法栈(Native Method stack)
堆(heap),程序计数器(program counter Register)
其中本地方法栈是已经超过了java的代码范围了,和c反而会更接近一些了
执行引擎-----本地库接口-----本地方法库
接下来分别解释以下以下的一些作用,
程序计数器是一块小的内存空间,它可以看做是当前线程所执行的字节码的行号指示器(这里和汇编那个程序计算器,类似理解即可)
字节码解释器工作时就是通过改变这个计数器的值来进行选取下一条需要执行的字节码指令,分支,循环以及跳转,异常处理,线程恢复等基础功能都是依赖于这个程序计数器进行完成的。
java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的(时间片轮转算法来进行多线程的切换的)
任何时刻,一个处理器都只会执行一条线程中的指令,因此,为了线程切换后恢复到正确执行位置,每条线程需要一个独立的程序计数器,各线程之间的计数器互不影响,独立存储,称这类内存区域为“线程私有的”内存
如果线程正在执行一个java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址,如果是正在执行native方法,这个计数器值则为空,native是已经超越了java的执行方法,此时计数器的值则为空(?Undefined),此内存区域是唯一一个在java的虚拟机规范当中没有规定任何outofMemoryError的区域。
Java虚拟机栈
虚拟机栈也是线程私有的,他的生命周期是与线程相同的,虚拟机栈描述的是java方法执行的内存模型。每个方法在执行的时候会创建一个栈帧,用于存储局部变量表,操作数栈,动态链接,方法出口等信息。每一个方法从调用直至执行完成的过程,就对应一个栈帧在虚拟机栈中入栈到出栈的过程。
从入栈到出栈的过程对应着一个生命周期的完成与结束
经常有人把java的内存分为堆内存和栈内存,这种分法是十分粗糙的,java内存的划分比这还要复杂很多
而这里的栈指的就是虚拟机栈
如果线程请求的深度大于虚拟机,则会抛出stack overflowError异常,如果虚拟机可以动态扩展。扩展无法申请到足够的内存的时候就会抛出outofMemoryError异常。
本地方法栈
与虚拟机所发挥的作用是非常相似的,虚拟机栈为虚拟机执行的java方法(字节码)但是本地方法栈则为虚拟机使用到的native方法服务,有的虚拟机是将本地方法栈和虚拟机方法栈混为一体,与虚拟机方法栈一样,本地方法栈也会抛出stackoverflowError
和outofMemoryError异常
堆()是java虚拟机所管理内存最大的一块,java堆是所有的线程共享的一块内存区域,在虚拟机启动的时候创建,
此栈内存的唯一目的是存放对象实例,几乎所有的对象实例都在这里分配内存。
随着JIT发展,这个貌似变得不那么绝对了
java堆是垃圾收集器管理的主要区域,因此很多时候也被称为GC堆,由于现在的收集是基本采用分代收集算法,因此java堆中还可以细分为:新生代和老年代
结论:无论如何细分,都和存放内容无关,无论哪个区域,存储的都是对象实例,进一步划分是为了更好地回收内存。
或者是为了更好地分配内存。
根据虚拟机规范定义可以得知,java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的就可以了。和电脑操作系统的磁盘整理情况是类似的,在物理空间上不一定是连续的,但是在逻辑上是连续的。
当前的虚拟机都是按照可扩展来实现的,如通过Xmx以及Xms来进行控制的。
如果在对内存中没有完成实例分配,并且堆也无法再进行扩展的时候,将会抛出outofMemoryError异常。
方法区:
也是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息,常量,静态变量,虽然java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个non-heap(非堆),目的应该是与java堆分开来。
友情提示JDK1.8之后。Xx:Maxpersize就没有了在配置加载信息中,自己曾经对于内存进行调优过。
堆内存是依赖于机器的物理内存的,但是有些 内存是具有上限的。不可以随物理内存而进行改变。