JVM运行时数据区

目录

什么是JVM运行时数据区?

一. 程序计数器(Program Counter Register)

二,虚拟机栈(VM Stack)

三,本地方法栈(Native Method Stack)

四,堆(Heap)

 五,方法区(Method Area)


什么是JVM运行时数据区?

我们都知道,当java文件编译成class文件后,需要交给JVM去运行,那么肯定是会存在一个数据运行区,我们的写的方法中的变量包括方法本身,都是在运行时数据区中被操作的,下面我们先来看看运行时数据区的数据组成图。

一. 程序计数器(Program Counter Register)

      在JVM中,也可以这么理解,程序计数器是指当前线程执行时的字节码指令地址,相当于行号

      为什么需要记录这个地址呢? 因为我们的线程在运行过程中不是持续能够抢占到资源的,在多线程的情况下,线程之间通过轮流切换时间片来完成需要执行的任务,所以程序计数器的作用就是,当线程再次获得资源时,将执行当前记录的字节码对应的地址。

二,虚拟机栈(VM Stack)

     存储当前线程执行运行方法时所需要的数据,指令和返回地址等,如下图:

     一个方法一个栈帧,对于我们熟悉的递归方法,将会创建n个方法, 如果异常,将会遇到StackOverFlowError。JVM的栈可以动态扩展(例如我们熟知的ArryList, 便是可支持扩展的),但是在尝试扩展时无法申请到足够的内存则抛出OutOfMemoryError异常。

局部变量表的详解,可查看:https://blog.csdn.net/qq_15037231/article/details/96462457

cd到类对应的class文件路径下,反编译命令 javap -v TestJVM > 0726.txt,可查看结果如下:

class文件中包含对应的字节码,可以查看文章https://blog.csdn.net/Ginny_2017/article/details/97371018

三,本地方法栈(Native Method Stack)

 本地方法栈和虚拟机栈相似,区别就是虚拟机为虚拟机栈执行Java服务(字节码服务),而本地方法栈为虚拟机使用到的Native 方法服务。本地方法栈中使用的语言,使用方式,数据结构没有强制要求。在HotSpot虚拟机实现中是把本地方法栈和虚拟机栈合二为一的,同理它也会抛出StackOverflowError和OOM异常。

四,堆(Heap)

       堆是JVM里最大的一块内存区域,被所有线程共享,在虚拟机启动时创建,此区域的目的就是存放对象实例和数组,几乎所有的对象实例都在这分配(随着JIT的发展已经不是那么绝对了)的.java堆是垃圾收集管理的主要区域,由于现在收集器基本都采用分代收集方法,所以Java的堆中还可以分为新生代,老年代,永久代.1.8之后取消了永久代;其中新生代又划分为Eden空间,From Survivor空间,To Survivor空间。比如堆的分配内存为无论怎么划分都是为了更好的回收,分配,利用内存。

1. 新生代与老年代的比例1:2, 而新生代中Eden:From:To = 8:1:1

2. from区和to区会随着每一次的gc而调换位置,同时当新生代的eden空间不足时,如果该对象无法通过gc释放,将会被移动到老年代中,因为老年代对新生代存在担保机制, 关于担保机制的条件,可参照文章:https://blog.csdn.net/qq_36187285/article/details/82144696

下图为1.8后的内存模型:

 

 五,方法区(Method Area)

   方法区也是一个线程共享的区域,存储已被虚拟机加载的类信息,常量(final),静态变量(static),JIT(即时编译器)编译后的代码等数据。

Java虚拟机规范把方法区描述为堆的一个逻辑部分,其实堆和方法区可以看成数据部分;虚拟机栈和程序计数器可以看成指令部分;方法区存储一些不会变更的数据,之前热点上使用GC分代收集管理方法区,所以方法区也被称为永久代(本质上两者不等价),但是现在已经使用Native Memory来代替永久代了。

    虚拟机对方法区规范非常宽松,除了和Java的堆一样不需要连续的内存和可以选择固定大小意外,还可以选择不实现垃圾回收。垃圾回收行为在这个区域比较少见但还是有必要的,主要是针对常量池回收和类型的卸载。

5.1运行时常量池

    运行时常量池是方法区的一部分,用于存放编译期生成的各种字面量和符号引用,运行时常量池相对于类常量池另外一个特性就是具备动态性,运行期间可能将新的常量放入池中。 

*************************************************************************************

public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    int i = 0;
    while (true) {
        list.add(String.valueOf(i++).intern());  
    }
}
*************************************************************************************

实际上对于以上代码,在JDK6、JDK7、JDK8运行结果均不一样。原因就在于字符串常量池在JDK6的时候还是存放在方法区(永久代)所以它会抛出OutOfMemoryError:Permanent Space;而JDK7后则将字符串常量池移到了Java堆中,上面的代码不会抛出OOM,若将堆内存改为20M则会抛出OutOfMemoryError:Java heap space;至于JDK8则是纯粹取消了方法区这个概念,取而代之的是”元空间(Metaspace)“,所以在JDK8中虚拟机参数”-XX:MaxPermSize”也就没有了任何意义,取代它的是”-XX:MetaspaceSize“和”-XX:MaxMetaspaceSize”等。

参考自:https://www.cnblogs.com/yulinfeng/p/7153391.html

参考资料:《深入浅出JVM》

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值