【JVM】运行时数据区——自问自答


Q:Java 运行时数据区解构,哪些数据线程独占,哪些是线程共享?每个区域会产生GC和异常吗?

运行时数据区:
1、PC寄存器
2、堆区
3、JVM栈
4、Native栈
5、方法区

其中,PC寄存器、Native栈、JVM栈是线程独占的。
      堆区、方法区是线程共享的。
      
      PC寄存器 没有垃圾回收,没用任何异常
      Native栈、JVM栈会抛出StackOverFlowError 和OutOfMemory:Stack 异常,没有GC
      
      堆区分为新生代和老年代,新生代中的Eden区满触发YGC/Minor GC ,老年代满触发Major GC。
      堆区有OutOfMemory :heap 异常
      
      方法区:存在GC (Full GC),在JDK7 以前 存在OutOfMemory:PremGen
      JDK8及其以后是 OutOfMemory:MetaSpace

【补充一条】 关于指令

零地址指令、基于栈的指令、基于寄存器的指令。

java字节码之所以能实现跨平台,主要是因为字节码可以被操作系统之上的JVM虚拟机识别,执行。

普通机器指令是基于CPU寄存器的,由于CPU架构不同,导致各个指令集有差异,有一地址,二地址等,这个地址指的就是寄存器地址:

比如 用汇编助记符表示: MOV 0x02  10 ;//这里写的很不严谨,只是为了好理解,这个指令第一个Mov表示操作为将数10,移动地址为0x02的寄存器上。

而零地址,是用基于栈的指令来实现的,因为栈是虚拟的数据结构,并不是实际的硬件。并且,它不需要地址来标记操作数,因为可以通过入栈和出栈来完成移动的效果。

基于栈的指令集就可以避免不同CPU架构造成的指令集不同引起的跨平台性。

但是基于栈的指令集也是有缺点的。

寄存器指令集:指令集大,但是完成一个操作所需要的指令少

栈指令:指令集小,完成一个操作所需要的指令很多,相对效率就低。

Q:具体说一说PC寄存器

设计参考CPU的PC寄存器,目的是存储指令相关的现场信息。

java的PC寄存器 准确的说理解为 指令计数器 (也有人翻译成程序钩子)。

 看一下这里的起始PC,其实就是指令对应的编号(地址),也就是将要被放在PC上的数据

 作用:PC 寄存器用来存储指向下一条指令的地址,也即将执行的指令代码。由执行引擎读取下一条指令。

PC上,没用GC,也没有任何异常。


Q:具体说一说虚拟机栈

一个线程有一个JVM栈,当这个线程中进行一次方法调用的时候,会形成一个栈帧。压入这个栈中。
当这个方法执行结束的时候,栈帧会被弹出JVM栈。随着线程的生命周期结束,它对应的JVM栈也会被销毁。

JVM Stack中的栈帧的结构:

局部变量表、操作数栈、方法返回地址、动态链接 和一些附加信息。

JVMStack 不涉及垃圾回收。
它在以下情况会抛出异常:

1、当栈的大小是固定的,当前线程中一个方法中不断的去嵌套式的调用其他方法(比如递归),
从内存的角度来说,就是不断的有栈帧被压入当前线程的JVM栈中,如果最后一次栈帧所需要的空间不足时,会抛出 StackOverFlowError

2、当栈的大小是动态扩容的,当前栈进行扩容时,如果物理内存空间小于栈要扩充的空间,就会抛出OutofMemory :stack 异常


栈帧内的结构说明:
 

局部变量表:


定义为一个数字数组,主要用于存储方法参数和定义在方法体内的局部变量,
局部变量表在线程的栈上,是线程的私有数据,因此不存在数据安全问题。
局部变量表的容量大小是在编译期确定下来的(前段编译 javac 生成字节码),并保存在方法的Code属性的maximum local variables数据项中,
在方法运行期间是不会修改局部变量表的大小的。
在方法调用结束后,局部变量表会随着方法栈帧的弹出而销毁。

局部变量表的Slot,是其最基本的存储单元。
LV中存放着编译期可知的各种基本数据类型(8种),引用类型(reference),returnAddress类型的变量。
LV中,32位内的类型只占用一个slot,64位的类型(long double)占两个slot
    byte short char 都会被转成int boolean也会转成int 0false 非0 表示true (字节码指令集里可以看到 bint,sint)
    
普通对象方法和构造函数的LV中,index0的变量是 this,而静态方法没用。

slot是可以被复用的比如:

public class SlotTest{public void localVar(){int a =0;Sout(a);int b=0;}public void localVar(){{int a =0; //a作用域在代码块中,出离了代码块,a这个局部变量就失去作用域,从LV角度来看,a的Slot位就空了,而下面的局部变量b可以対之复用Sout(a);}    int b=0;}}

需要注意的一点是,局部变量表不存在系统默认初始化的过程,(这一点区别于静态变量)

所以,使用局部变量时,务必手动初始化。

局部变量也是可达性分析的根节点,只要是被局部变量表中直接或间接引用的对象,都不会被回收。


操作数栈、


方法返回地址、


动态链接 


Q:具体说一说Java的堆


Q:具体说一说Java的方法区

Q:类加载系统

        //单独开文

Q:执行引擎的结构和工作

        //单独开文


Q:String的不可变性分析

首先说明String的几个问题:

String 是被final修饰的,表示它不可再被继承

jdk8及其以前 String 内部是 final  char[] value,Jdk9 之后改成了 byte[]

真正想解释这个所谓的不可变性,还是得从JVM内存结构来分析。

不可变性的表现: 对字符串重新赋值时,需要重新制定内存区域赋值,不能使用原来的value进行赋值。

当对现有字符串进行链接操作时,也需要重新指定内存区域赋值,不能使用原来的value进行赋值。

调用String的replace() 修改指定字符或字符串时,同上

1、字面量创建一个字符串的过程:

此时字符串的值声明在字符串常量池中。

pc =0 ldc #2是什么?:

 

 从运行时常量池中取一个元素进操作数栈。

紧接着:astore_1  表示 将操作数栈的引用型变量 保存入局部变量表index1的位置

如果运行时常量池中的条目是字符串常量,即对string类实例的引用,则将对该实例的引用value压入操作数栈。

注意点:

字符串常量池不会存储相同的字符串。

String的 String pool 是一个固定大小的Hashtable,默认长度是 1009,若放入的String 过多,会导致hash冲突严重,从而导致链表很长,链表最大的特点就是插入删除简单,但是遍历时间复杂度高,这样就导致调用String.intern性能大幅下降(intern是会先查重,再插入的,有个遍历过程)

可以使用-XX:StringTableSize设置StringTable的长度

jdk6 StringTableSize 默认1009,大小可以随便修改

jdk7 StringTableSize 默认是60013 大小可以随便设置

jdk8开始,设置StringTableSize长度,不可低于1009

2、new String("xxx")的过程

   public void test2(){String ok666 = new String("OK666");}

整个过程是 创建对象,从常量池里找到字符串实体对象的引用,通过构造函数给新建对象赋值。

所以:一次 new String(“XX”) 其实底层有两个对象

1、new 关键字在堆空间创建的对象

2、字符串常量池中的对象,字节码指令为 ldc

3、字符串连接符 + 

1、常量与常量的拼接结果在常量池,原理是编译期的优化

2、常量池中不会存在相同内容的常量

3、只要其中一个是变量,结果就存在堆中。变量拼接的底层原理是Stringbuilder

4、如果拼接的结果调用了intern(),则主动将常量池中还没有的字符串对象放入常量池中,并返回此对象的地址。

常量拼接

 @Testpublic void test3(){String s1 ="a"+"b"+"c";  //常量拼接String s2 ="abc";System.out.println(s1 == s2); //true}

 带变量的拼接

 @Testpublic void test3(){String s1 ="a"+"b"+"c";  //常量拼接String s2 ="abc";String s3 =s2+"test";System.out.println(s1 == s2); //true}
字节码执行流程如下:

含有变量的字符串拼接操作,用的是Stringbuilder 对象,append()操作。 最后 toString()返回,存入局部变量表

问题:在new String("a")+new String("b")中,一共创建了几个对象?

5个:

new StringBuilder()

new String("a")

常量池里的a

常量池里的b

new String("b")

new String("ab")

特别强调!!

Stringbuilder的toString()被调用,并不会在常量池中生成 "ab"实例

这里有个细节:

通过查看字StringBuilder的toString()节码, 我们发现他和普通的new String()并不一样。它没用ldc指令。

这个是StringBuilder的 toString(),它的内部看上去好像就是new String(),但是看看他的字节码

它并不是普通的new String("xx")构造法,会用ldc 从常量池加载一个字面量对象的引用变量入栈,直接进行 invokespecial <java/lang/String.<init>>操作。因此常量池在字节码编译期并没有创建一个字面常量对象 "ab"。

可以对比参考 2、new String("xxx")的过程

String intern()的使用

jdk6 中将这个字符串对象尝试放入常量池

 + 如果池中有,并不会放入。返回已有的池中对象的地址。

+ 如果没有,会把对象复制一份,放入池中,并返回池中对象的地址

jdk7 起 中将这个字符串对象尝试放入常量池

 +  如果池中有,并不会放入。返回已有的池中对象的地址。

+   如果没有,则会把对象的引用地址复制一份,放入池中,并返回池中的引用地址。

之所以jdk7有这个改变,我分析原因如下:

jdk6(发布时间 2006年12月) Stringtable 和静态变量还是在堆外的,严格的放在方法区中(永久代)

 jdk7(发布时间 2011年7月) 中    Oracle收购JRocket虚拟机(2008年)之后,重新定义了关于JVM的一些落地实现方案,

它把Stringtable 和静态变量移到了堆内,虽然这个时候方法区还是用的JVM内存,落地实现还叫永久代。

由于字符串常量已经放在了堆中,为了节省堆内存空间,就没用必要再在堆内常量池中再创建一个副本对象了,直接通过指针引用就可以了。

Q:i++和++i的底层理解

**首先,我是真的想问候一下出这种面试题的人,你们的目的何在?你认为一个普通程序员会去关注底层字节码吗?刷存在感呢?让程序员死记硬背这种问题有意思吗,活该你们一辈子当资本鹰犬,当奴才。

   public void test1(){int i =10;i++;System.out.println(i);}

上述方法:局部变量表最大slot数是2 index0 =this,index1 =i;

操作数栈的最大深度 2

i++ 具体执行过程如图示

 

 此时,index1位置的变量 i 变成 11

再看看++i

会发现执行结果是一致的:

 

这里总结一句:

自加操作在底层字节码表示的方式是一致的,但是要注意,自加指令是 inc index   by 1

这个操作是在局部变量表中完成的,并没有移入移出栈。这个原则会导致下面现象发生:

  public void test3(){int i =10;i=i++;System.out.println(i);}

输出结果是10,这里有个关键问题就是,自加操作,对变量i本身再次复制:

赋值操作实际上是将 操作数栈中最新的变量,去覆盖对应局部变量表中旧的变量。

pc =3 iload_1 指把LV中 index1 的变量i 取到操作数栈中,

pc=4 iinc 1 by 1 ,LV中 index1 进行自加操作

pc=7 将操作数栈的内容写回 LV index=1的位置。

所以,此时出现取出i=10, i在局部变量表中自加(LV index1 i=11),写回i=10,重新覆盖了 局部变量表中的 i.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/45360.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何在pycharm中指定GPU

如何在pycharm中指定GPU 作者:安静到无声 个人主页 目录 如何在pycharm中指定GPU打开编辑配置点击环境变量添加GPU配置信息推荐专栏在Pycharm运行程序的时候,有时候需要指定GPU,我们可以采用以下方式进行设置: 打开编辑配置 点击环境变量 添加GPU配置信息 添加名称:CU…

geacon_pro配合catcs4.5上线Mac、Linux

我的个人博客: xzajyjs.cn 一些链接 Try师傅的catcs4.5项目: https://github.com/TryGOTry/CobaltStrike_Cat_4.5&#xff0c;最新版解压密码见&#xff1a;https://www.nctry.com/2708.html geacon_pro: https://github.com/testxxxzzz/geacon_pro BeaconTool.jar: https:/…

【LLM评估篇】Ceval | rouge | MMLU等指标

note 一些大模型的评估模型&#xff1a;多轮&#xff1a;MTBench关注评估&#xff1a;agent bench长文本评估&#xff1a;longbench&#xff0c;longeval工具调用评估&#xff1a;toolbench安全评估&#xff1a;cvalue&#xff0c;safetyprompt等 文章目录 note常见评测benchm…

ubuntu20搭建环境使用的一下指令

1.更新源 sudo vim etc/apt/sources.listdeb http://mirrors.aliyun.com/ubuntu/ xenial main deb-src http://mirrors.aliyun.com/ubuntu/ xenial maindeb http://mirrors.aliyun.com/ubuntu/ xenial-updates main deb-src http://mirrors.aliyun.com/ubuntu/ xenial-updates…

Redis实现共享Session

Redis实现共享Session 分布式系统中&#xff0c;sessiong共享有很多的解决方案&#xff0c;其中托管到缓存中应该是最常用的方案之一。 1、引入依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM…

openGauss学习笔记-44 openGauss 高级数据管理-存储过程

文章目录 openGauss学习笔记-44 openGauss 高级数据管理-存储过程44.1 语法格式44.2 参数说明44.3 示例 openGauss学习笔记-44 openGauss 高级数据管理-存储过程 存储过程是能够完成特定功能的SQL语句集。用户可以进行反复调用&#xff0c;从而减少SQL语句的重复编写数量&…

SpringBoot 学习(03): 弱语言的注解和SpringBoot注解的异同

弱语言代表&#xff1a;Hyperf&#xff0c;一个基于 PHP Swoole 扩展的常驻内存框架 注解概念的举例说明&#xff1b; 说白了就是&#xff0c;你当领导&#xff0c;破烂事让秘书帮你去安排&#xff0c;你只需要批注一下&#xff0c;例如下周要举办一场活动&#xff0c;秘书将方…

sql server安装报错 合成活动模板库(ATL) 失败

错误 “合成活动模板库(ATL) 规则失败“ 解决办法&#xff1a; 进入SQL Server 2008R2安装包目录找到文件&#xff1a;sqlsupport_msi&#xff0c;安装此文件之后&#xff0c;再安装SQL Server&#xff0c;便可解决该问题。C:\SQL Server 2008R2\new\SQL Server 2008R2\2052_CH…

java Spring Boot yml多环境拆分文件管理优化

上文 java Spring Boot yml多环境配置 我们讲了多环境开发 但这种东西都放在一起 还是非常容易暴露信息的 并且对维护来讲 也不是非常的友好 这里 我们在resources下创建三个文件 分别叫 application-pro.yml application-dev.yml application-test.yml 我们直接将三个环境 转…

Android 广播发送流程分析

在上一篇文章中Android 广播阻塞、延迟问题分析方法讲了广播阻塞的分析方法&#xff0c;但是分析完这个问题&#xff0c;自己还是有一些疑问&#xff1a; 广播为啥会阻塞呢&#xff1f;发送给接收器就行了&#xff0c;为啥还要等着接收器处理完才处理下一个&#xff1f;由普通…

JVM前世今生之JVM内存模型

JVM内存模型所指的是JVM运行时区域&#xff0c;该区域分为两大块 线程共享区域 堆内存、方法区&#xff0c;即所有线程都能访问该区域&#xff0c;随着虚拟机和GC创建和销毁 线程独占区域 虚拟机栈、本地方法栈、程序计数器&#xff0c;即每个线程都有自己独立的区域&#…

帆软大屏2.0企业制作

&#xfffc; 数字化观点中心 / 当前页 如何从0-1制作数据大屏&#xff0c;我用大白话给你解释清楚了 文 | 商业智能BI相关文章 阅读次数&#xff1a;18,192 次浏览 2023-06-08 11:51:49 好莱坞大片《摩天营救》中有这么一个场景&#xff1a; &#xfffc; 你可以看见反派大b…

使用Nginx调用网关,然后网关调用其他微服务

问题前提&#xff1a;目前我的项目是已经搭建了网关根据访问路径路由到微服务&#xff0c;然后现在我使用了Nginx将静态资源都放在了Nginx中&#xff0c;然后我后端定义了一个接口访问一个html页面&#xff0c;但是html页面要用到静态资源&#xff0c;这个静态资源在我的后端是…

PyTorch模型性能分析与优化

动动发财的小手&#xff0c;点个赞吧&#xff01; 训练深度学习模型&#xff0c;尤其是大型模型&#xff0c;可能是一项昂贵的支出。我们可以使用的管理这些成本的主要方法之一是性能优化。性能优化是一个迭代过程&#xff0c;我们不断寻找提高应用程序性能的机会&#xff0c;然…

Springboot 实践(10)spring cloud 与consul配置运用之服务的注册与发现

前文讲解&#xff0c;完成了springboot、spring security、Oauth2.0的继承&#xff0c;实现了对系统资源的安全授权、允许获得授权的用户访问&#xff0c;也就是实现了单一系统的全部技术开发内容。 Springboot是微服务框架&#xff0c;单一系统只能完成指定系统的功能&#xf…

NSSCTF之Misc篇刷题记录(14)

[SWPUCTF] 2021新生赛之Crypto篇刷题记录① [UUCTF 2022 新生赛]王八快跑[安洵杯 2020]BeCare4[HDCTF 2023]ExtremeMisc[SUCTF 2018 招新赛]follow me[SUCTF 2018 招新赛]佛家妙语 NSSCTF平台&#xff1a;https://www.nssctf.cn/ PS&#xff1a;记得所有的flag都改为NSSCTF […

【Linux取经路】探索进程状态之僵尸进程 | 孤儿进程

文章目录 一、进程状态概述1.1 运行状态详解1.2 阻塞状态详解1.3 挂起状态详解 二、具体的Linux操作系统中的进程状态2.1 Linux内核源代码2.2 查看进程状态2.3 D磁盘休眠状态(Disk sleep)2.4 T停止状态(stopped) 三、僵尸进程3.1 僵尸进程危害总结 四、孤儿进程五、结语 一、进…

C++初阶——string(字符数组),跟C语言中的繁琐设计say goodbye

前言&#xff1a;在日常的程序设计中&#xff0c;我们会经常使用到字符串。比如一个人的身份证号&#xff0c;家庭住址等&#xff0c;只能用字符串表示。在C语言中&#xff0c;我们经常使用字符数组来存储字符串&#xff0c;但是某些场景(比如插入&#xff0c;删除)下操作起来很…

git版本管理加合并笔记

1.创建空文件夹&#xff0c;右键Bash here打开 2.打开链接&#xff0c;点击克隆下载&#xff0c;复制SSH链接 3.输入git SSH链接 回车 遇到问题&#xff1a; 但明明我已经有权限了&#xff0c; 还是蹦出个这 4.换成https在桌面上进行克隆仓库就正常了 5.去vscode里改东西 …

删除远程桌面的下拉框ip地址

原因&#xff1a; 如下图&#xff0c;有时候想清理掉无法连接的IP。 方法&#xff1a; 一、进入 注册表编辑器 进入方法&#xff1a;一下两个方法都可以使用。 1. 在win10里面直接搜索 注册表编辑器&#xff0c;然后打开 2. 打开 运行&#xff08;Win R&#xff09;&#xff…