面试常问点:深入剖析JVM的那些事

Posted king哥Java架构

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试常问点:深入剖析JVM的那些事相关的知识,希望对你有一定的参考价值。

先前知识

众所周知java是一种跨平台的语言,但实际上跨平台的并不是java而是JVM。

JVM(Java Virtual Machine)是一种虚拟机,用来将由java文件编译成的class字节码文件再编译成机器语言,供机器识别。有了JVM中间人的存在就不需要直接与操作系统打交道,且不同的操作系统有不同的JVM,于是就屏蔽了操作系统间的差异,从而使java成为跨平台语言。

DVM又是什么?

Dalvik Virtual Machine简称DVM也是一种虚拟机,是专门为android平台开发的,它与JVM是有差别的。

Dalvik基于寄存器,而JVM 基于栈。性能有很大的提升。基于寄存器的虚拟机对于更大的程序来说,在它们编译的时候,花费的时间更短。

寄存器的概念

寄存器是中央处理器内的组成部分。寄存器是有限存贮容量的高速存贮部件,它们可用来暂存指令、数据和位址。在中央处理器的控制部件中,包含的寄存器有指令寄存器(IR)和程序计数器(PC),在中央处理器的算术及逻辑部件中,包含的寄存器有累加器(ACC)

栈的概念

栈是线程独有的,保存其运行状态和局部自动变量的(所以多线程中局部变量都是相互独立的,不同于类变量)。栈在线程开始的时候初始化(线程的Start方法,初始化分配栈),每个线程的栈互相独立。每个函数都有自己的栈,栈被用来在函数之间传递参数。操作系统在切换线程的时候会自动的切换栈,就是切换SS/ESP寄存器。栈空间不需要在高级语言里面显式的分配和释放。

JVM

java的使用流程:

  • 1、编写.java文件

  • 2、编译成.class

  • a、打包成.jar(Java Archive) .war(Web Archive)使用

  • b、命令行则直接使用.class

其实.jar和.war是.class文件的压缩包,其中还包含了不同的配置文件,使用时通过类加载器取其内部的.class字节码文件加载到JVM。

Class文件结构

JVM接收的最初数据是class字节码文件,由.java文件编译产生。并不是只有java语言可以编译成class文件,其他语言也是可以(Scala、Groovy)。

class文件结构

class文件是由8位为一组的字节为基本单位,构成的二进制文件,为什么是二进制文件呢?

  • 1、机器语言为二进制,所以使用便捷;

  • 2、占用空间小,3.1415927用文本文件存储需要将各个位转成ASCII码再存储需占用9字节,二进制文件存储只需四字节;

  • 3、存储数据精确不会丢失。

结构如上图,最上方为起始位,内容包含了.java文件的信息。

类型

class文件中只有两种类型:无符号数和表

无符号数为基本类型,有:u1、u2、u4、u8,数字代表字节数。无符号数可以代表数字、索引引用、数量值,或者按照UTF-8编码构成字符串值

表则是由基本类型和表构成的类型,属于组合类型

magic

文件最初的4个字节,称为魔数,是计算机识别文件类型的依据,不同于感官,.word .png .avi这种通过扩展名识别文件类型的方式,计算机识别多数文件是通过文件头部的魔数。

这种做法的优点在于安全性,文件的扩展名可以人为随意的修改,也许并不会造成文件的不可用(早年间的“图种”一词不知多少人有经历),也可能造成文件不可用,但文件的类型在文件创建之初就被赋予魔数的话,就可以大限度的保证文件的安全性。

version

表示此.class的版本信息,有minor_version和major_version两种类型共占了4字节。

不同版本Java有不同的特性,产生的class结构也会不同,JVM通过识别版本从而确定是否可识别此文件。JVM是向下兼容的,如果.class版本过高则不能运行(Unsupported major.minor version **)。

constant
constant_pool为常量池,用来存放常量,constant_pool_count为池中的计数。

constant_pool的索引从1开始,当指针不想引用此constant_pool时则将指针指向0,此操作简单(赋值永远比删除简单)。

常量池中有两大类常量类型:字面量和符号引用

  • 1、字面量为java中的基本数据类型

  • 2、符号引用:以一组符号来描述所引用的目标,引用的目标并不一定已经加载到内存中,在类加载过程的解析阶段JVM将常量池内的符号引用替换为直接引用。类型有:
    1、CONSTANT_Class_info 类和接口的全限定名(该名称在所有类型中唯一标识该类型)
    2、CONSTANT_Fieldref_info 字段的名称和描述符
    3、CONSTANT_Methodref_info 方法的名称和描述符

常量池中每一项常量都是一个表

常量类型

讲解:

class A{  
    int i=9;  
    int b=new B();  
}  
  
class B{  
}  

编译时会产生A.class和B.class,此时A.class有两个常量9和B。JVM加载A.class时,将由于常量9属于字面量即基本数据类型,直接放入常量池。

到常量B时,由于常量B不属于字面量即基本数据类型,所以此时产生一个符号引用来代表常量B。

等到A.class加载到了解析阶段,需要将符号引用改为直接引用,但找不到符号引用B的直接引用,

在使用阶段,由于A对象主动引用了B类,所以JVM通过类加载器开始加载B.class(同样的加载步骤),并创建了B对象,并将符号引用B改为B对象的直接引用。

access

access_flags即java中的类修饰符

类的身份信息

一个类要有类名,关系要有extends和implement。java中类是单继承,所以除Object外所有类都有一个父类,而接口则可以有多实现。

this_class是这个类的全限定名
super_class是这个类父类的全限定名
interfaces是这个类实现接口的集合
interfaces_count是这个类实现接口的数量

fields

fields_count表示fields表中的数量

fields是表结构用来存放字段,字段即为类中声明的变量。字段包括了类级变量或实例级变量,static修饰符为判断依据。

public static final transient String str = "Hello World";  

一个字段包含的信息有:

  • 1、作用域(public、private、protected修饰符)

  • 2、类级变量还是实例级变量(static修饰符)

  • 3、可变性(final)

  • 4、并发可见性(volatile修饰符,是否强制从主内存读写)

  • 5、可否序列化(transient修饰符)

  • 6、字段数据类型(基本类型、对象、数组)

  • 7、字段名称(str)

一个字段有多种修饰符,每种修饰符只有两种状态:有、没有,所以采用标志位来表示最为合理。字段的其他信息,叫什么名字、被定义为什么数据类型,这些都是无法固定的,所以引用常量池中的常量来描述。

字段结构

access_flags:修饰符

修饰符标志位

name_index:简单名称
指变量名,存放在常量池。例如字段str的简单名称“str”。

descriptor_index:描述符
描述字段的类型

类型列表

例子:
java.lang.String[][] —— [[Ljava/lang/String
int[] —— [I
String s —— Ljava/lang/String

attributes:属性集合,以用于描述某些场景专有的信息。

上面的类型只定义了变量信息,那变量的初始赋值操作呢?

赋值操作是将常量赋值给变量,常量有字面量和符号引用,字面量会在常量池中,符号引用依据情况会在解析或使用阶段改为直接引用。

字段赋值的时机:
a:对于非静态的field字段的赋值将会出现在实例构造方法()中
b:对于静态的field字段,有两个选择:

1、在类构造方法()中进行;
2、使用ConstantValue属性进行赋值
编译器对于静态field字段的初始化赋值策略:

如果final和static同时修饰一个字段,并且这个字段是基本类型或者String类型的,
那么编译器在编译这个字段的时候,会在对应的field_info结构体中增加一个ConstantValue类型的结构体,在赋值的时候使用这个ConstantValue进行赋值;

如果该field字段并没有被final修饰,或者不是基本类型或者String类型,那么将在类构造方法中赋值。

对于全局变量的值是被编译在构造器中赋值的

https://www.cnblogs.com/straybirds/p/8331687.html

methods

methods_count表示methods表中的数量

methods是表结构用来存放方法,表结构和字段的表结构一致

由于部分关键字相对于变量和方法是有区别的

例子:
int indexOf(char[] source,int sourceOffset,int sourceCount,char[] targetOffset,int targetCount,int fromIndex) —— ([CII[CII)I

方法内部的代码存到什么地方了?

attributes:属性表

在字段表和方法表中都有属性表

属性表结构

属性表所能识别的属性有

方法中到具体代码就存放在方法表中属性表的Code属性中

参考 https://blog.csdn.net/sinat_37138973/article/details/54378263

其实不同成长阶段也会有不同的技术能力要求,就像JVM,只有达到一定的职业高度,才会有它很重要的认知,只有弄清楚虚拟机底层原理,才能走向更高的境界。所以掌握JVM是逐渐成长为高级Java工程师的过程中不可或缺的一部分,想要升职涨薪、跳槽大厂面试加分,就必须弄清JVM的原理与调优。
图片
图片
在面试中,常会问一些JVM调优的面试题,其中包括内存泄漏、字符串、垃圾回收等方面。尤其是
大厂,十分爱考察求职者对底层执行原理的掌握
,看你到底水平有多高,如果没有对JVM进行深入的了解以及实践,那你就有很大几率会在面试中“out”,而且在要薪酬上也比较吃亏

那么如何才能快速掌握JVM的原理与应用呢?又该怎样在面试环节拿下JVM相关的面试题,让面试官刮目相看?

学习JVM,可以让你更深入理解Java语言,还可以让你学会用基础的知识解决难题,提高工作效率。
在这里插入图片描述
学习完JVM可以落地的知识:

  • 根据自己的业务场景选择适合自己的GC实现
  • 观察gc频率优化内存的分配策略
  • 根据dump日志来分析程序瓶颈位置或找出崩溃原因
  • 根据JIT编译优化流程、代码

此外,还能让你的代码在写法上,更安全、更高效,避免内存溢出的问题。随着对JVM的掌握越来越多,你的收入水平以及技术能力都逐渐向高级开发工程师迈进。

适合这样的你

如果你:

  • 很想掌握JVM,学会解决JVM内存溢出、内存泄露、JVM调优等问题
  • 想对Java有更深入的理解,提升Java性能
  • 希望面试时能够从容应对JVM的相关问题
  • 想要突破25k月薪的门槛
  • 正在向高级开发工程师发展进阶

在这里插入图片描述

一、JVM内存区域划分

在这里插入图片描述

二、JVM执行子系统

在这里插入图片描述

三.垃圾回收器和内存分配策略

在这里插入图片描述

四、编写高效优雅Java程序

在这里插入图片描述

五、性能优化

在这里插入图片描述

JVM面试题总结

在这里插入图片描述

程序员必备书单

《Java核心知识点合集(283页)》

内容涵盖:Java基础、JVM、高并发、多线程、分布式、设计模式、Spring全家桶、Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、MongoDB、Redis、mysql、RabbitMQ、Kafka、Linux、Netty、Tomcat、数据库、云计算等
在这里插入图片描述

《Java中高级核心知识点合集(524页)》

在这里插入图片描述

《Java高级架构知识点整理》

在这里插入图片描述

《1000道 互联网Java工程师面试题 (485页)》

在这里插入图片描述

Java各知识点综合面试专题(1000+题)

这套题库里面中包含了以下很多个模块(都有单独的PDF文档):并发编程,多线程,集合框架,设计模式,数据库,性能优化,RabbitMQ消息中间件,ActiveMQ消息中间件,Dubbo,JVM,Kafka,MongoDB,MyBatis,MySQL,Netty,nginx,Redis,Tomcat,Zookeeper,Spring,SpringBoot,SpringCloud,SpringMVC,…….
在这里插入图片描述

因为文章内容实在是太多了,不能够给大家一一体现出来,每个章节都有更加细化的内容。大家需要完整版文档的小伙伴,可以一键三连,下方获取免费领取方式!在这里插入图片描述

以上是关于面试常问点:深入剖析JVM的那些事的主要内容,如果未能解决你的问题,请参考以下文章

开智小站-面试强化系列深入剖析JVM

升职加薪必看!那些年Java面试官常问的知识点

Java软件开发面试题!Java岗大厂面试官常问的那些问题

前端程序员不可不知的 —— 浏览器的那些事(全都是干货!!!)

前端程序员不可不知的 —— 浏览器的那些事(全都是干货!!!)

Apache软件基金会核心项目Tomcat的那些事