java heap 结构_JVM内存结构

一、JVM内存结构概览

Java虚拟机在执行Java程序的过程中会将其管理的内存划分为若干个不同的数据区域,这些区域有各自的用途、创建和销毁的时间,有些区域随虚拟机进程的启动而存在,有些区域则是依赖用户线程的启动和结束来建立和销毁。Java虚拟机所管理的内存包括以下几个运行时数据区域,如图:

Java虚拟机启动之后会将某个内存空间划分一个运行时数据区,该区域里分布着上述五个区域,五个内存区域中方法区和堆是线程共享的,其他三个区域是每个线程在执行程序是自己独有的内存数据区,这也就说明方法区和堆中存放的内容可以给所有线程共同访问的,现在我们来看每个区域的详细解释:

二、Java堆(Heap)

Java对象存储的地方。Java堆是虚拟机管理的内存中最大的一块;

Java堆是所有线程共享的区域;

在虚拟机启动时创建;

此内存区域的唯一目的就是存放对象实例,几乎所有对象实例都在这里分配内存。存放new生成的对象和数组;

Java堆是垃圾收集器管理的内存区域,因此很多时候称为“GC堆”。

Java堆是垃圾回收器主要管理的区域,现在对堆的垃圾回收机制都是采用分代收集算法:JDK 1.7及以前的,有永久代

Java堆的内存划分如图所示,分别为Young Gen(年轻代)、Old Memory(老年代)、Perm(永久代)。

其中在JDK1.8中,永久代被移除,使用MetaSpace代替。

1、新生代使用复制算法(Copying算法),原因是年轻代每次GC都要回收大部分对象。新生代里面分成一份较大的Eden空间和两份较小的Survivor空间。每次只使用Eden和其中一块Survivor空间,然后垃圾回收的时候,把存活对象放到未使用的Survivor(划分出from、to)空间中,清空Eden和刚才使用过的Survivor空间;

分为Eden、Survivor From、Survivor To,比例默认为8:1:1;

内存不足时发生Minor GC。

2、老年代采用标记-整理算法(Mark-compact),原因是老年代每次GC只会回收少部分对象;

老年代几乎都是从survivor中熬过来的,不会轻易“死掉”,因此Major GC不会像Minor GC那样频繁。

3、永久代

用来存储类的元数据,也就是方法区。永久代的废除:在JDK 1.8中,永久代被替换成MetaSpace,MetaSpace存放在本地内存中。原因是永久代进场内存不够用,或者发生内存泄漏。

MetaSpace(元空间):元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。

4、本地内存(Native Memory)

也叫堆外内存、直接内存,堆外内存即不是JVM的,是操作系统的内存,不会影响GC。

三、方法区(Method Area)

存放加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

在Java类的生命周期的“连接-准备”阶段,就是将这些信息加载到方法区的。

方法区也需要进行空间管理,这个区域的内存空间管理主要是针对常量池的回收和对类型的卸载,毕竟在类被加载的时候就意味着上述这些数据要分配到方法区当中,那同样当方法区的内存空间不够再分配的时候,也会出现OutOfMemoryError异常。

四、JVM栈(JVM Stack)

每个方法被执行的时候会先在这个栈内存中创建一个栈帧,用来存储局部变量表、操作数栈、动态链接、方法出口等信息,每个方法从开始到执行完成的过程,就对应着一个栈帧在JVM栈中从入栈到出栈的过程。

局部变量表

存放编译期就已经知道的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用,这里的的对象引用不是指对象本身,不同的虚拟机它可能是一个指向对象起始地址的引用指针,也可能指向一个代表对象的句柄或者其他与此对象相关的位置和returnAddress类型(指向了一条字节码指令的地址),对象访问过程就会涉设计到局部变量表。

其中64位长度的long和double类型的数据会占用2个局部变量空间(Slot),其余的数据类型只占用1个。

局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。

操作数栈

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

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

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

虚拟机把操作数栈作为它的工作区——大多数指令都要从这里弹出数据,执行运算,然后把结果压回操作数栈。比如,iadd指令就要从操作数栈中弹出两个整数,执行加法运算,其结果又压回到操作数栈中。

动态链接

Class文件的常量池中存在有大量的符号引用,字节码中的方法调用指令就以指向常量池的引用作为参数。部分符号引用在类加载阶段(解析)的时候就转化为直接引用,这种转化为静态链接;

部分符号引用在运行期间转化为直接引用,这种转化为动态链接。

方法出口

方法退出的方式分两种:

(1)正常完成出口

执行引擎执行任意一个方法返回(如:return)的字节码指令,这时候会有返回值返回方法的调用者。一般来说,正常退出时,调用者的PC计数器的值可以作为放回地址。

(2)异常完成出口

在方法执行的过程中遇到了异常,且没有在方法体中进行处理。异常完成出口退出时,不会给上层调用者任何返回值。

方法退出实际上等同于当前栈帧出栈,因此一般过程为:恢复上层方法的局部变量表和操作数栈;

把返回值压入调用者栈帧的操作数栈中;

调整PC计数器的值以指向后面一条指令。

JVM栈异常

如果不断的进行方法里面调用另一个方法,或者无线递归调用的情况下,极限情况也会出现内存问题,因为JVM栈也是一块内存空间,也存在大小限制,那不断地创建栈帧然后入栈,当栈的深度大于虚拟机所允许的深度时,会抛出StackOverflowError(栈溢出)异常,这是其中的一种内存问题,虚拟机栈空间不够的时候大部分虚拟机是支持动态扩展的,即使扩展,但是在无限入栈然后扩展再扩展,当无法再扩展足够的内存时,就会出现与堆一样可能存在的OutOfMemoryError异常。

所以JVM栈是可能出现两种异常的,一种是栈的大小超过虚拟机限定的大小又不允许扩展栈大小时会出现StackOverflowError(栈溢出)异常,另一种是栈大小不够并且无法再扩展新的空间时会出现OutOfMemoryError异常。

五、本地方法栈(Native Method Stack)

与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。

也是栈结构,所以也会存在两种异常情况:StackOverflowError 和 OutOfMemoryError 异常。

在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一。

六、程序计数器(Program Counter Register)

记录对应线程执行到哪了。

专业的描述就是:当前线程所执行的字节码的行号指示器,字节码解释器工作时就是通过改变这个程序计数器的指向来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

为什么需要这么个东西呢?首先大家知道程序在计算机中跑的时候,线程利用CPU资源的时候是有时间限制的,就是CPU是不断切换着线程去执行线程对应任务的,如果没有程序计数器的话,那某个线程在执行某个方法的时候,还没执行完这个方法CPU就切换到另外一个线程去执行了,那再次切回到这个线程的时候,这个线程就不知道上次是做了啥做到哪了,这是非常不合理的,也就是针对这个问题,才有了程序计数器来记录线程执行到了哪一行的字节码,以便在线程被挂起之后再次获取到CPU资源时能够从上次断开的位置继续执行。

程序计数器的空间是比较小的,它的职责很明确,从它的职责来看空间也没必要多大,这个内存区域也是五个区域中唯一一个没有内存异常的,因为它只是记录线程执行到哪里了,这样的记录信息不存需要很大的空间容量,每个线程都有其自己的程序计数器并且私有,线程间互不干扰。

原文链接:

如有错误,请更正指出,谢谢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值