认真阅读思考就能深入认识JVM

Posted chai-blogs

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了认真阅读思考就能深入认识JVM相关的知识,希望对你有一定的参考价值。

一、JVM基础(JVM是什么?  JRE是什么? JDK是什么?)

a)         JVM是什么?

编写的Java文件编译成class文件,class文件放入JVM中转义机器码,让机器执行

                         i.              Java跨平台:一次编译到处运行原理,是因为安装了不同文件操作系统的JDK(JVM), 字节码(class)文件适配不同底层的操作系统(不同操作系统的文件操作/描述符不同,不同操作系统的句柄也不同),所以通过字节码(class)文件和JVM帮我们屏蔽了底层的操作系统,是字节码(class)文件做中间语言实现的跨平台,所以结论:JVM软件层面屏蔽了底层硬件、指令层面的细节。

(知识扩展,C/C++是跨平台的吗?是,但这是层次问题,原因是:C/C++ 是通过编译器编译实现的跨平台,需要在编译级别去兼容底层不同的操作系统和不同实现,所以C/C++编写代码时不同操作系统下的编码不同)

 技术图片

 

 

                        ii.              如上图所示:JVM屏蔽了所以的Platforms(平台),JVM是JRE中的一部分,JVM分为两部分:Client VM和Server VM,JDK1.8以后就没有了Client VM (因为机器是64位操作系统,支持内存理论可达无限,而Client VM最大只能支持内存4G,原因:机器是二进制存储,如果系统是32位,那2^32=4G,操作系统最大只能支持4G)

 技术图片

 

 

 

b)         JRE是什么?

JRE(Java Runtime Environment) :JVM + Java运行时的底层类库(大部分是使用C/C++写的)

c)         JDK(Java Development ToolKit) : JRE + 编辑Java文件所需要的编译器和监控JVM的监控工具

二、为什么要学JVM?

因为面试造火箭,工作拧螺丝,玩笑,继续

a)         为什么Java从1995年支撑到如今?为什么Java在企业级开发占据领域?为什么Java在开发语言中占据第一?

是因为内存管理,因为编写Java不需要关注内存问题,不需要关心创建对象后回收问题,所以我们的精力可以专注在业务实现上面;相比C/C++,比如创建数组还需要malloc分配内存,并且还需要自己手动free释放内存

b)      但As the saying goes, every coin has two sides(俗话说,每个硬币都有两面),因为在编写代码时不需要关注内存管理时,所以更需要了解内存问题。

如果发生内存出现问题或内存泄漏/溢出,无知将无措,有追求的程序员要知其然知其所以然,所以要学JVM。

三、JVM运行时数据区

a)         什么是运行时数据区?学了有什么用?怎么学?

运行时数据区:顾名思义,代码在运行时创建的数据区。运行时数据区又分为数据区和指令区

 技术图片

 

 

 

Java类内容只包含:数据、指令、控制三种(常量、静态变量、成员变量为数据;运算是指令;结束返回是控制)

(知识扩展:计算机也包含三流[数据流、指令流、控制流])

b)         程序计数器:类似与计算机指令寄存器存储在RAM里,指向当前线程正在执行的字节码指令的地址(行号):Java最小的执行单位是线程,线程执行指令,操作系统CPU运行指令,CPU运行指令是由不稳定的调度策略调度,而调度策略是基于轮转时间片(执行顺序是抢占式),所以当前时间点分配给哪个指令是不确定的,未抢占成功的指令将会挂起,而线程主要作用是运行指令(不记录当前指令运行状态),指令状态是由程序计数器记录的。指令要恢复执行状态就要去找到自己私有的程序计数器中的地址(行号)

程序技术器是线程私有/独享的一个变量

c)         虚拟机栈:

推导:虚拟机栈——》栈——》数据结构——》存储数据

解释:存储当前线程运行方法时所需要的数据、指令、返回地址

                         i.              类方法是由线程执行,而线程是一个执行者,不会存储数据。线程执行类方法时的数据结构就是虚拟机栈

                       ii.              虚拟机栈FILO(first-in last-out,先进后出):基础单位是栈帧,其中一个方法就是一个栈帧,每个栈帧长度不一。栈帧包含:局部变量表、操作数栈、动态链接、出口等;

  1. 局部变量表:是存储执行方法时的变量和对应的值,是一个在编译时期就能知道大小的定长(32位)区块, 2^32=4G寻址空间;
  2. 操作数栈:是存储执行方法时的操作数或中间结果(存储方法运行时暂存的操作数据);
  3. 动态链接:是Java具备动态特性运行时多态,执行方法解析成员变量是接口(此接口被其他类实现)时,该成员变量调用方法(此接口方法被实现的类的方法)的过程可以理解位时动态链接
  4. 出口:每个执行的方法都会有一个return结束方法,因为方法入栈就必须要出栈,出栈有两种方法:正常出栈,异常出栈

引申:当一个方法A调用另一个方法B时,在虚拟机栈内的位置是B在A的前面(因为栈是先进后出)

类成员引用变量值是存储在Heap(堆)内,所以在执行方法应用到成员引用变量时会有栈指向堆的操作(栈帧内局部变量表存储成员引用变量是存储指向堆的地址)

                      iii.              虚拟机栈是有大小的,超出大小会报StackOverflowError错误

d)         本地方法栈:带native修饰的方法是本地方法(本地类库,大多由C/C++语言编写),过程可对比虚拟机栈

e)         方法区:根据版本呢划分,版本<=1.6存储的是类信息、常量、静态变量(常量、静态变量是存储在方法区内,成员引用变量是存储在堆内,局部变量是存储在虚拟机栈的栈帧内;版本>=1.7,字符串常量迁移到堆内

f)          Head(堆)

线程共享:方法区和head(堆)

线程独享:程序计数器、虚拟机栈、本地方法栈

 技术图片

 

 

 

如上逻辑图不是存储在线程内是存储在寄存器内

四、JVM内存模型

 技术图片

 

 理想状态下不触发大对象threshold,不触发age TLAB

新生代是复制回收算法

当申请一个8M的空间会放入到eden区,如果程序运行过程中再去申请一个1M的空间,那正在使用的8M空间会进入空间担保,进入老年代,新生代eden会进行一次Minor gc(基于新生代是复制回收算法,就会触发一次Minor gc/young gc),恢复原有空间,并把1M的空间放入eden区。

技术图片

 

 最大的变化就是把永久代变成Meta Space(元空间)

优点:再也不需要受到perm size 的限制,Meta Space 是可以无限扩容

缺点:Meta Space不属于堆,Meta Space不可控(会抢占别的区块空间)

假设内存有2G,元空间有256M,Heap(堆)Xms 256m/Xms 1g,当元空间自动扩容超过1G以上,那heap(堆)空间就会异常

 技术图片

 

 缺点解决方案:定义best practice MetaSpace大小

(知识扩展:

         数据要存储,那数据就要回收,回收就要有垃圾回收算法

         垃圾回收算法——》垃圾回收器

垃圾回收算法决定了垃圾回收器,不同的区块选用不同的垃圾回收算法,也就是选用不同的垃圾回收器)

以上是关于认真阅读思考就能深入认识JVM的主要内容,如果未能解决你的问题,请参考以下文章

深入认识二进制序列化--记一次生产事故的思考

《深入理解Java虚拟机 - Jvm高级特性与最佳实践(第三版)》阅读笔记

《深入理解Java虚拟机 - Jvm高级特性与最佳实践(第三版)》阅读笔记

轻松认识JVM运行时数据区域(使用思维导图)

深入理解JVM阅读笔记-内存溢出小结

关于尽快看清问题本质的思考