JVM运行时数据区

JVM运行时数据区

运行时数据区主要包括五个部分:方法区, 堆,虚拟机栈,本地方法栈,程序计数器

线程共享区线程私有区
方法区 , 堆本地方法栈,虚拟机栈,程序计数器

程序计数器

程序计数器是一个较小的内存空间,看作是当前线程执行的字节码的行号指示器,负责记录选择下一条需要执行的字节码指令,每个线拥有不同的程序计数器,分别记录不同线程程序字节码的执行位置,通过程序计数器来实现分支,跳转,循环,异常处理,线程回复等基础功能。

java虚拟机栈

虚拟机栈也是线程私有的,虚拟机栈中存放了编译器产生的基本数据类型,和对象引用(地址),返回程序字节码地址等,大多数情况下指虚拟机的局部变量表部分。

Java 虚拟机规范允许Java栈的大小是动态的或者是固定不变的。

  • 如果采用固定大小的Java虚拟机栈,那每一个线程的Java虚拟机栈容量可以在线程创建的时候独立选定。如果线程请求分配的栈容量超过Java虚拟机栈允许的最大容量,Java虚拟机将会抛出一个StackOverflowError 异常。
  • 如果Java虚拟机栈可以动态扩展,并且在尝试扩展的时候无法申请到足够的内存,或者在创建新的线程时没有足够的内存去创建对应的虚拟机栈,那Java虚拟机将会抛出一个 OutOfMemoryError 异常。

本地方法栈

本地方法栈与虚拟机栈的主要区别为本地方法栈只存储虚拟机使用的native本地方法,本地方法就是不是由java代码编写的需要通过接口调用 c++ 代码的方法。《java虚拟机规范》对本地方法栈中方法使用的语言,使用方式与数据结构并没有强制规定。

与虚拟机栈一样,本地方法栈也会在栈深度溢出或栈扩展失败时分别抛出 StackOverflowError 和 OutOfMemoryError 异常。

堆是一块线程共享的区域,也是虚拟机管理的内存中最大的区域。堆的唯一目的就是存储对象实例 ,也是垃圾回收器GC管理的区域。《java虚拟机规范》中描述堆:‘’所有的对象实例以及数组都应该在堆上分配“ 现在已经能看到些许迹象表明日后可能出现值类型的支持,即使只考虑现在,由于即时编 译技术的进步,尤其是逃逸分析技术的日渐强大,栈上分配、标量替换[2]优化手段已经导致一些微妙 的变化悄然发生,所以说Java对象实例都分配在堆上也渐渐变得不是那么绝对了。

方法区

方法去与堆一样是一块线程共享区域,它用于存储一些被虚拟机加载的一些类型信息,常量,静态变量,即时编译器编译的代码缓存等数据。

在jdk1.8之前方法区

在JDK1.7及以前,HotSpot虚拟机将Java类信息、常量池、静态变量、即时编译器编译后的代码等数据,存储在Perm(永久带)里(对于其他虚拟机如BEA JRockit、IBM J9等是不存在永久带概念的),类的元数据和静态变量在类加载的时候被分配到Perm里,当常量池回收或者类被卸载的时候,垃圾收集器会回收这一部分内存,但效果不太理想。
JDK 1.8中则把永久代给完全删除了,取而代之的是元空间(MetaSpace),将类元数据放到了本地内存中,将常量池和静态变量放到了Java堆里,HotSpot VM将会为类的元数据明确的分配与释放本地内存,在这种架构下,类元数据就突破了-XX:MaxPermSize的限制,所以此配置已经失效,现在可以使用更多的本地内存。这样一定程度上解决了原来在运行时生成大量的类,从而经常Full GC的问题——如运行时使用反射、代理等。

运行时常量池

运行时常量池是方法区的一部分。

常量池在内存中的位置:

  • 在JDK1.6中,方法区是以永久代的方式实现(HotSpot),常量池是方法区的一部分。
  • 在JDK1.7中,方法区合并到堆内存中,常量池可以说在堆内存中。
  • 在JDK8中,方法区又被剥离出来,只不过实现方式不是永久代,此时的方法区叫元数据区,常量池也就在元数据区。

常来讲,所有变量,包括基本类型和引用类型,他们都存在虚拟机栈中,包括变量类型、变量名称、和变量值。对于基本类型来说,值就是具体的值;而对于引用类型来说,值是指对象实例在堆内存中对应的地址。

对于引用类型的数据,如果没有常量池,那么就会反复在堆内存中创建和销毁值相同的对象,这样有损系统性能。相比之下,基本类型的变量就不会有这样的弊端,所以一般不会放到常量池中,直接在栈中操作即可。

常量池的作用是避免频繁地创建和销毁值相同的对象,节省内存空间,节省运行时间。比如,需要已存在的字符串,直接从常量池中取即可,无需重新创建。

常量池:五大基本类型对应的包装类的常量池、String字符串常量池。

基本类型包装类常量池

基本数据类型不会存储在常量池中,因为一般的运算、判断和赋值都是对值的操作,直接在虚拟机栈中进行,不涉及到对象,也就与堆内存无关(也就涉及不到对象的创建以及垃圾回收),不会对系统性能造成太大的影响。

Java中的 Byte, Short, Integer, Long, Character, Boolean五种包装类实现了常量池技术。只有数值处在[-128,127]这个范围的包装类对象能存到常量池当中,超过这个范围的对象都要在堆内存中创建。利用new创建的包装类对象仍然存在堆内存中。

字符串常量池

String str1 = “a”; 用引号直接初始化的字符串str1属于字符串常量。编译期就已经放入到常量池中,str1是一个引用,str1的值是“a”在常量池中的地址。

String str2 = new String(“a”); 此时的str2是字符串变量,其指向的对象实例在堆内存中;指向的字符串对象也可以变。类加载和运行时都会处理上述语句。

类加载时,先判断常量池中是否已经有“a”,如果有,则什么都不做;如果没有,则创建一个“a”放在常量池中。

运行时,执行到new这个语句,是将常量池中的实例复制一份放到堆内存中,也无需重新创建,但str2的值是实例在堆内存中的地址。虽然不是创建了两个对象,但是确实是新增了两个新的对象。

上述涉及到类加载的过程未必准确,但结果相同,变量指向的是堆内存中的对象。

个人博客查看更多内容

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值