面试常问点:深入剖析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的那些事的主要内容,如果未能解决你的问题,请参考以下文章
前端程序员不可不知的 —— 浏览器的那些事(全都是干货!!!)