想要去阿里面试?你必须得跨过 JVM 这道坎!

概述

 

 

不少人想要到阿里巴巴、美团、京东等互联网大公司去面试,可是如今互联网大厂面试通常都一定会考核JVM相关的知识积累和实践经验,毕竟线上系统写好代码部署以后,每一个工程师都必须关注JVM相关的东西,好比OOM、GC等问题.java

 

因此一块儿来看看JVM的最基本的区域划分以及工做原理,这个基本上是互联网公司面试必问。程序员

 

 

区域划分

 

jvm的区域划分以下所示:面试

 

 

大体就是分为:程序计数器,虚拟机栈,堆,方法区,本地方法栈,这几个部分。网络

 

接下来咱们从本身写好的Java代码如何经过JVM来运行的角度,来分析一下JVM里这些区域是如何支撑咱们的Java代码跑起来的。并发

 

 

程序计数器

 

假设咱们有以下的一个类,就是最最基本的一个HelloWorld而已:jvm

 

public class HelloWorld {性能

        public static void main(String[] args) {spa

            System.out.println("Hello World");操作系统

        }线程

}

 

上面那段代码首先会存在于 “.java” 后缀的文件里,这个文件就是java源代码文件,可是这个文件是面向咱们程序员的,计算机他是看不懂你写的这段代码的

 

因此此时就得经过编译器,把“.java”后缀的源代码文件编译为“.class”后缀的字节码文件。

 

这个“.class”后缀的字节码文件里,存放的就是对你写出来的代码编译好的字节码了,这个字节码才是计算器能够理解的一种语言,而不是咱们写出来的那一堆代码。

 

这个字节码看起来大概是下面这样的:

 

 

这段字节码并非彻底对照着HelloWorld那个类来写的,就是给一段示例,让你们知道“.java”翻译成的“.class”是大概什么样子的。

 

这里好比说“0: aload_0”这样的,就是“字节码指令”,他对应了一条一条的机器指令,计算机只有读到这种机器码指令,才知道具体应该要干什么。

 

好比说字节码指令可能会让计算机从内存里读取某个数据,或者把某个数据写入到内存里去,都有可能,各类各样的指令,就会指示计算机去干各类各样的事情。

 

因此如今首先明白一点,咱们写好的Java代码是会被翻译成字节码的,对应各类字节码指令。

 

那么Java代码经过JVM跑起来的第一件事情就明确了, 首先Java代码被编译出来的字节码指令必定会被一条一条的执行,这样才能实现咱们写好的代码被执行的效果。

 

那么在执行字节码指令的时候,JVM里的程序计数器就是用来记录每一个线程当前执行的字节码指令的位置的,记录当前线程目前执行到了哪一条字节码指令。

 

由于会有多个线程来并发的执行各类不一样的代码,因此每一个线程都有本身的一个程序计数器,专门记录当前这个线程目前执行到了哪一条字节码指令了

 

下图更加清晰的展现出了他们之间的关系。

 

 

 

Java虚拟机栈

 

Java代码在执行的时候,必定是线程来执行某个方法中的代码,好比哪怕就是上面的那个最基础的HelloWorld代码,也会有一个main线程来执行main方法里的代码。

 

在方法里,常常会定义一些方法内的局部变量,好比下面这样,就在方法里定义了一个局部变量“name”。

 

public void sayHello() {

        String name = "hello";

}

 

因此JVM必须有一块区域是来保存每一个方法内的局部变量等等数据的,这个区域就是Java虚拟机栈

 

每一个线程都会去执行各类方法的代码,方法内还会嵌套调用其余的方法,因此首先每一个线程都有本身的Java虚拟机栈。

 

若是线程执行了一个方法,那么就会被这个方法调用建立对应的一个栈帧,栈帧里就有这个方法的局部变量表 、操做数栈、动态连接、方法出口等东西,可是这里别的不太好理解,先理解一个局部变量就能够。

 

好比说一个线程调用了上面写的“sayHello”方法,那么就会为“sayHello”方法建立一个栈帧,压入线程本身的Java虚拟机栈里面去。

 

在栈帧的局部变量表里就会有“name”这个局部变量,下图展现了这个过程。

 

 

接着若是“sayHello”方法调用了另一个“greeting”方法 ,好比下面那样的代码:

 

 

那么这个时候会给“greeting”方法又建立一个栈帧压入线程的Java虚拟机栈里,由于开始执行“greeting”方法了,并且“greeting”方法的栈帧的局部变量表里会有一个“greet”变量,这是“greeting”方法的局部变量。

 

 

 

接着若是“greeting”方法执行完毕了,就会把“greeting”方法对应的栈帧从Java虚拟机栈里给出栈,而后若是“sayHello”方法也执行完毕了,就会把“sayHello”方法也从Java虚拟机栈里出栈。

 

这就是JVM中的 “ Java虚拟机栈 ” 这个组件的做用,调用执行任何方法的时候,都会给方法建立栈帧而后入栈。

 

而在栈帧里存放了这个方法对应的局部变量之类的数据,包括这个方法执行的其余相关的信息,方法执行完毕以后就出栈。

 

 

Java堆内存

 

JVM中有另一个很是关键的区域,就是Java堆,这里就是存放咱们在代码中建立的各类对象的,好比说下面的代码:

 

public void teach(String name) {

    Student student = new Student(name);

    student.study();

}

 

上面的 “new Student(name)” 这个代码就是建立了一个Student类型的对象实例,这个对象实例里面会包含一些数据。

 

好比说这个Student的“name”就是属于这个对象实例的一个数据,那么相似Student这样的对象,就会存放在Java堆内存里。

 

Java堆内存区域里会放入相似Student的对象,而后方法的栈帧的局部变量表里,这个引用类型的“student”局部变量就会存放Student对象的地址。

 

至关于你能够认为局部变量表里的“student”指向了Java堆里的Student对象。

 

看下图会更加清晰一些。

 

 

 

方法区 / Metaspace

 

这个方法区是在JDK 1.8之前的版本里,表明JVM中的一块区域,主要是放相似Student类本身的信息的,平时用到的各类类的信息,都是放在这个区域里的,还会有一些相似常量池的东西放在这个区域里。

 

可是在JDK 1.8之后,这块区域的名字改了,叫作“Metaspace”,能够认为是“元数据空间”这样的意思,这里固然主要其实仍是存放咱们本身写的各类类相关的信息。

 

 

本地方法栈

 

其实在JDK不少底层API里,好比IO相关的,NIO相关的,网络Socket相关的,若是你们去看他内部的源码,会发现不少地方都不是Java代码了。

 

不少地方都会去走native方法,去调用本地操做系统里面的一些方法,可能调用的都是c语言写的方法,或者一些底层类库,好比下面这样的:

 

public native int hashCode();

 

在调用这种native方法的时候,就会有线程对应的本地方法栈,这个里面也是跟Java虚拟机栈相似的,也是存放各类native方法的局部变量表之类的信息。

 

 

堆外内存

 

还有一个区域,是不属于JVM的,经过NIO中的allocateDirect这种API,能够在Java堆外分配内存空间。

 

而后经过Java虚拟机里的 DirectByteBuffer 来引用和操做堆外内存空间,其实不少技术都会用这种方式,由于有一些场景下,堆外内存分配能够提高性能。

 

 

总结

 

最后作一点总结,咱们的Java代码经过JVM来运行的时候,首先必定会一行一行执行编译好的字节码指令。

 

而后在执行的过程当中,对于方法的调用,会经过Java虚拟机栈来为每一个方法建立栈帧入栈和出栈,并且栈帧里有方法的局部变量表

 

接着对于对象的建立,会分配到Java堆内存里去

 

对于类信息的存储,会放在方法区 / Metaspace这样的区域里。

 

另外有两块特殊的区域:

  • 本地方法栈,是执行native方法时候用的栈,跟Java虚拟机栈是相似的

     

  • 堆外内存,是能够在Java堆外分配内存空间来存储一些对象。

相关文章
相关标签/搜索