JVM-运行时数据区

常量池分类

1、静态常量池

静态常量池时相对于运行时常量池来说的,属于描述class文件结构的一部分
由字面量和符号引用组成,在类在加载后会将静态常量池加载到内存中也就是运行时常量池。
字面量:文本,字符串以及final修饰的内容
符号引用:类,接口,方法,字段等相关的描述信息

2、运行时常量池

当静态常量池被加载到内存后就会变成运行时常量池
也就是真正的文件的内容落到JVM内存中

3、字符串常量池

**设计理念:**字符串作为最常用的数据类型,为减小内存的开销,专门为其开辟了一块内存区域用于存放
JDK1.6及之前版本,字符串常量池是位于永久代(相当于现在的方法区)
JDK1.7之后,字符串常量池位于Heap堆中
面试常问点:(笔试居多)

下列三种操作最多产生哪些对象

1.直接赋值

String a ="aaaa";

解析:

最多创建一个字符串对象。

首先“aaaa”会被认为字面量,先在字符串常量池中查找(.equals()),如果没有找到,在堆中创建“aaaa”字符串对象,并且将“aaaa”的引用维护到字符串常量池中(实际是一个hashTable结构,存放key-value结构数据),再返回该引用;如果在字符串常量池中已经存在“aaaa”的引用,直接返回该引用。

2.new String()

String a =new String("aaaa");

解析:

最多会创建两个对象。

首先“aaaa”会被认为字面量,先在字符串常量池中查找(.equals()),如果没有找到,在堆中创建“aaaa”字符串对象,然后再在堆中创建一个“aaaa”对象,返回后面“aaaa”的引用;
3.intern()

String s1 = new String("yzt");
String s2 = s1.intern();
System.out.println(s1 == s2); //false

解析:

String中的intern方法是一个 native 的方法,当调用 intern方法时,如果常量池已经包含一个等于此String对象的字符串(用equals(object)方法确定),则返回池中的字符串。否则,将intern返回的引用指向当前字符串 s1(jdk1.6版本需要将s1 复制到字符串常量池里)

常量池在内存中的布局
在这里插入图片描述

运行时数据区

在装载的第(2)(3)步可以发现由运行时数据,堆,方法等名词
(2)将这个字节流所代表的静态存储结构转换为方法区的运行时数据结构
(3)在java堆中生成一个代表这个类的java.lang.Class对象,作为堆对方法区中这些数据的访问入口
说白了就是类文件被类加载器装载进来之后,类中的内容(比如变量,常量,方法,对象等这些数据的要有个去处,也就是要存储起来,存储的位置肯定是在JVM中有对应的空间)
在这里插入图片描述

1、Method Area 方法区

(1)方法区是各个线程共享的内存区域,在虚拟机启动时创建
(2)虽然java虚拟机规范把方法区描述为堆的一个逻辑部分,但是他却又有一个别名叫做非堆,目的是与java堆区分开
(3)用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据
(4)当方法区无法满足内存分配需求时,会抛出OutOfMemoryError异常

此时回看装载阶段的第二步,将这个字节流所代表的静态存储结构转换为方法区的运行时数据结构,如果这时候把从Class文件到装载的第一步和第二部合并起来理解的话,如下图
在这里插入图片描述值得说明的
JVM运行时数据区是一种规范,真正的实现在JKD8中就是Metaspace,在jdk6,7中就是perm Space

2、Heap 堆

(1)java堆是java虚拟机所管理内存中最大的一块,在虚拟机启动时创建,被所有线程共享
(2)java对象实例以及数组都在对上分配

此时会看装载阶段的第三步,在java堆中生成一个代表这个类的java.lang.Class对象,作为堆方法区这些数据的访问入口
在这里插入图片描述

3、Java Virtual Machine Stacks(虚拟机栈)

经过上面的分析,类加载机制的装载过程已经完成,后续的链接,初始化也会相应的生效
假如目前的阶段是初始化完成了,后续做啥呢?那就是使用,

(1)虚拟机栈是一个线程执行的区域,保存着一个线程中方法的调用状态,换句话说,一个java线程的运行状态,由一个虚拟机栈来保存,所以虚拟机栈肯定是线程私有的,独有的,随着线程的创建而创建

(2)每一个被线程执行的方法,为该栈中的栈帧,即每个方法对应一个栈帧,调用一个方法,就会向栈中压入一个栈帧,一个方法调用完成,就会把该栈帧从栈中弹出

3.1 栈帧

栈帧:每个栈帧对应一个被调用的方法,可以理解为一个方法的运行空间
每个栈帧中包含:局部变量表,操作数栈,指向运行时常量池的引用,方法返回地址,附加信息

局部变量表:方法中定义的局部变量以及方法的参数存放在这张表中
局部变量表中的变量不可直接使用,如需要使用的话,必须通过相关的指令将其加载至操作数栈中作为操作数使用

操作数栈:以压栈和出栈的方式存储操作数的

动态链接:每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态链接

方法返回地址:当一个方法开始执行后,只有两种方式可以退出,一种是遇到方法返回的字节码指令,一种是与建议异常,并且这个异常没有在方法体内得到处理
在这里插入图片描述

4、The pc Register(程序计数器)

我们都知道一个JVM进程中有多个线程在执行,而线程中的内容是否能够拥有执行权,是根据CPU调度来的
假如线程A正在执行到某个地方,突然失去了CPU的执行权,切换到线程B了,然后当线程A在获得CPU执行权的时候,怎么继续执行呢?这就是需要在线程中维护一个变量,记录线程执行的位置

如果线程正在执行java方法,则计数器记录的是正在执行的虚拟机字节码指令的地址

如果正在执行的是Native方法,则这个计数器为空

5、Native Method Stacks(本地方法栈)

如果当前线程执行的方法是Native类型的,这些方法就会在本地方法栈中执行
那如果在java方法执行的时候调用了native方法呢?
在这里插入图片描述

6、栈指向堆

如果在栈帧中有一个变量,类型为引用类型,这时候就是典型的栈中元素指定堆中对象
在这里插入图片描述

7、方法区指向堆

方法区中会存放静态变量,常量等数据,如果是下面的情况,就是典型的方法区元素指向堆中对象

private static Object obj = new Object();

在这里插入图片描述

8、堆指向方法区

方法区中包含类的信息,堆中会有对象,那怎么知道对象是那个类创建的呢?

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值