还怕面试官问JVM吗?

请你谈谈你对JVM的理解?
JVM类加载器是怎么样的?有几种?
什么是OOM,什么是StackOverFlowError? 怎么分析?
JVM常用调优参数有哪写?
GC有几种算法?分别是怎么执行的?
你知道JProfiler吗,怎么分析Dump文件?

第一次看到这些真真实实的面试题的时候,我~

还怕面试官问JVM吗?

这都什么玩意???????

经过一段时间的研究!!接下来,我将以大白话从头到尾给大家讲讲Java虚拟机!!

不对的地方还请大家指正~

目录1、什么是JVM?在哪?2、JVM、JRE、JDK 的关系3、JVM体系结构4、三种JVM(了解)5、类加载器1、回顾new对象的过程2、类加载器的类别6、双亲委派机制1、什么是双亲委派机制2、作用7、沙箱安全机制什么是沙箱?java中的安全模型演进组成沙箱的基本组件1. `字节码校验器`(bytecode verifier)2. `类装载器`(class loader)8、Native本地方法接口9、PC寄存器10、方法区1. 方法区中有啥?2. 创建对象内存分析11、栈1、栈中存放啥?2、栈运行原理3、堆栈溢出StackOverflowError12、堆1、堆中有啥?2、堆内存详解1、Young 年轻代2、Tenured 老年代3、Perm 元空间3、什么是OOM?4、GC垃圾回收14、堆内存调优1、查看并设置JVM堆内存2、怎么排除OOM错误?1. 尝试扩大堆内存看结果2. 利用内存快照工具JProfiler3. 什么是Dump文件?如何分析?附:安装Jprofiler教程15、GC垃圾回收1、回顾2、GC算法1、引用计数算法(很少使用)2、复制算法3、标记–清除算法4、标记–整理算法总结思考:有没有最优的算法?

1、什么是JVM?在哪?

JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

百度的解释云里雾里,对于我们Java程序员,说白了就是:

JVM本质上是一个程序,它能识别.class 字节码文件(里面存放的是我们对.java编译后产生的二进制代码),并且能够解析它的指令,最终调用操作系统上的函数,完成我们想要的操作!
关于Java语言的跨平台性,就是因为JVM,我们可以将其想象为一个抽象层,只要这个抽象层JVM正确执行了.class文件,就能运行在各种操作系统之上了!这就是一次编译,多次运行

对于JVM的位置:

JVM是运行在操作系统之上的,它与硬件没有直接的交互

还怕面试官问JVM吗?

2、JVM、JRE、JDK 的关系

JDK(Java Development Kit):Java开发工具包

JRE(Java Runtime Environment):Java运行环境

JDK = JRE + javac/java/jar 等指令工具

JRE = JVM + Java基本类库

还怕面试官问JVM吗?

3、JVM体系结构

Java虚拟机主要分为五大模块:

类装载器子系统
运行时数据区
执行引擎
本地方法接口
垃圾收集模块

还怕面试官问JVM吗?

方法区是一种特殊的堆
栈里面不会有垃圾,用完就弹出了,否则阻塞了main方法
垃圾几乎都在堆里,所以JVM性能调优%99都针对于堆

4、三种JVM(了解)

Sun公司 HotSpot(我们都用的这个)
还怕面试官问JVM吗?
BEA公司 JRockit
IBM公司 J9 VM

5、类加载器

作用:加载.Class字节码文件

1、回顾new对象的过程

public class Student {
//私有属性
private String name;

//构造方法
public Student(String name) {
this.name = name;
}
}

类是模板、模板是抽象的;对象是具体的,是对抽象的实例化

//运行时,JVM将Test的信息放入方法区
public class Test{
//main方法本身放入方法区
public static void main(String[] args){
//s1、s2、s3为不同对象
Student s1 = new Student("zsr"); //引用放在栈里,具体的实例放在堆里
Student s2 = new Student("gcc");
Student s3 = new Student("BareTH");
System.out.println(s1.hashCode());
System.out.println(s2.hashCode());
System.out.println(s3.hashCode());
//class1、class2、class3为同一个对象
Class<? extends Student> class1 = s1.getClass();
Class<? extends Student> class2 = s2.getClass();
Class<? extends Student> class3 = s3.getClass();
System.out.println(class1.hashCode());
System.out.println(class2.hashCode());
System.out.println(class3.hashCode());
}
}

根据结果,我们发现:

s1、s2、s3的hashcode是不同的,因为是三个不同的对象,对象是具体的
class1、class2、class3的hashcode是相同的,因为这是类模板,模板是抽象的

我们画图分析以下new一个对象的流程:

    首先Class Loader读取字节码.class文件,加载初始化生成Student模板类
    通过Student模板类new出三个对象

还怕面试官问JVM吗?

那么Class Loader具体是怎么执行我们的.class字节码文件呢,这就引出了我们类加载器~

2、类加载器的类别

我们编写这样一个程序
还怕面试官问JVM吗?
根据返回结果,我们来讲解以下三种加载器:

级别从高到底

    启动类(根)加载器:BootstrapClassLoader

    c++编写,加载java核心库 java.*,构造拓展类加载器应用程序加载器

    根加载器加载拓展类加载器,并且将拓展类加载器的父加载器设置为根加载器

    然后再加载应用程序加载器,应将应用程序加载器的父加载器设置为拓展类加载器

    由于引导类加载器涉及到虚拟机本地实现细节,我们无法直接获取到启动类加载器的引用;这就是上面那个程序我们第三个结果为null的原因。

    加载文件存在位置

    还怕面试官问JVM吗?

    拓展类加载器:PlatformClassLoader

    java编写,加载扩展库,开发者可以直接使用标准扩展类加载器。
    java9之前为ExtClassloader,Java9以后改名为PlatformClassLoader
    加载文件存在位置

    还怕面试官问JVM吗?

    应用程序加载器:AppClassLoader

    java编写,加载程序所在的目录
    是Java默认的类加载器

    用户自定义类加载器:CustomClassLoader

    java编写,用户自定义的类加载器,可加载指定路径的class文件

6、双亲委派机制

1、什么是双亲委派机制

    类加载器收到类加载的请求
    将这个请求向上委托给父类加载器去完成,一直向上委托,直到根加载器BootstrapClassLoader
    根加载器检查是否能够加载当前类,能加载就结束,使用当前的加载器;否则就抛出异常,通知子加载器进行加载;自加载器重复该步骤。

2、作用

举个例子:我们重写以下java.lang包下的String类
还怕面试官问JVM吗?
发现报错了,这就是双亲委派机制起的作用,当类加载器委托到根加载器的时候,String类已经被根加载器加载过一遍了,所以不会再加载,从一定程度上防止了危险代码的植入!!

作用总结:

    防止重复加载同一个.class。通过不断委托父加载器直到根加载器,如果父加载器加载过了,就不用再加载一遍。保证数据安全。
    保证系统核心.class,如上述的String类不能被篡改。通过委托方式,不会去篡改核心.class,即使篡改也不会去加载,即使加载也不会是同一个.class对象了。不同的加载器加载同一个.class也不是同一个class对象。这样保证了class执行安全。

7、沙箱安全机制

这里引用了这篇博文引用链接,了解即可

什么是沙箱?

Java安全模型的核心就是Java沙箱(sandbox)

沙箱是一个限制程序运行的环境。沙箱机制就是将 Java 代码限定在虚拟机(JVM)特定的运行范围中,并且严格限制代码对本地系统资源访问,通过这样的措施来保证对代码的有效隔离,防止对本地系统造成破坏。
沙箱主要限制系统资源访问,系统资源包括CPU、内存、文件系统、网络。不同级别的沙箱对这些资源访问的限制也可以不一样。

所有的Java程序运行都可以指定沙箱,可以定制安全策略。

java中的安全模型演进

​ 在Java中将执行程序分成本地代码远程代码两种

本地代码可信任,可以访问一切本地资源。
远程代码不可信信在早期的Java实现中,安全依赖于沙箱 (Sandbox) 机制。

如下图所示

还怕面试官问JVM吗?

如此严格的安全机制也给程序的功能扩展带来障碍,比如当用户希望远程代码访问本地系统的文件时候,就无法实现。

因此在后续的 Java1.1 版本中,针对安全机制做了改进,增加了安全策略,允许用户指定代码对本地资源的访问权限。

如下图所示

还怕面试官问JVM吗?

Java1.2版本中,再次改进了安全机制,增加了代码签名

不论本地代码或是远程代码,都会按照用户的安全策略设定,由类加载器加载到虚拟机中权限不同的运行空间,来实现差异化的代码执行权限控制。

如下图所示

还怕面试官问JVM吗?

当前最新的安全机制实现,则引入了域 (Domain) 的概念。

虚拟机会把所有代码加载到不同的系统域应用域
系统域部分专门负责与关键资源进行交互
应用域部分则通过系统域的部分代理来对各种需要的资源进行访问。
虚拟机中不同的受保护域 (Protected Domain),对应不一样的权限 (Permission)。存在于不同域中的类文件就具有了当前域的全部权限,如下图所示

还怕面试官问JVM吗?

组成沙箱的基本组件
1. 字节码校验器(bytecode verifier)

​ 确保Java类文件遵循Java语言规范。这样可以帮助Java程序实现内存保护。但并不是所有的类文件都会经过字节码校验,比如核心类(如上述java.lang.String)。

2. 类装载器(class loader)

其中类装载器在3个方面对Java沙箱起作用

它防止恶意代码去干涉善意的代码;
它守护了被信任的类库边界;
它将代码归入保护域,确定了代码可以进行哪些操作。

虚拟机为不同的类加载器载入的类提供不同的命名空间,命名空间由一系列唯一的名称组成,每一个被装载的类将有一个名字,这个命名空间是由Java虚拟机为每一个类装载器维护的,它们互相之间甚至不可见。

类装载器采用的机制是双亲委派模式。

    从最内层JVM自带类加载器开始加载,外层恶意同名类得不到加载从而无法使用;
    由于严格通过包来区分了访问域,外层恶意的类通过内置代码也无法获得权限访问到内层类,破坏代码就自然无法生效。

存取控制器(access controller):存取控制器可以控制核心API对操作系统的存取权限,而这个控制的策略设定,可以由用户指定。
安全管理器(security manager):是核心API和操作系统之间的主要接口。实现权限控制,比存取控制器优先级高。
安全软件包(security package):java.security下的类和扩展包下的类,允许用户为自己的应用增加新的安全特性,包括:

    安全提供者
    消息摘要
    数字签名
    加密
    鉴别

8、Native本地方法接口

JNI:Java Native Interface

本地接口的作用是融合不同的编程语言为Java所用,它的初衷是融合C/C++程序
还怕面试官问JVM吗?

native:凡是带native关键字的,说明java的作用范围达不到了,会去调用底层c语言的库!进入本地方法栈,调用本地方法接口JNI,拓展Java的使用,融合不同的语言为Java所用

Java诞生的时候C、C++横行,为了立足,必须要能调用C、C++的程序
于是在内存区域中专门开辟了一块标记区域:Native Method Stack,登记Native方法
最终在执行引擎执行的的时候通过JNI(本地方法接口)加载本地方法库的方法

目前该方法使用的越来越少了,除非是与硬件有关的应用,比如通过Java程序驱动打印机或者Java系统管理生产设备,在企业级应用中已经比较少见。因为现在的异构领域间通信很发达,比如可以使用 Socket通信,也可以使用 Web service等等,了解即可!

9、PC寄存器

程序计数器: Program Counter Register

每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向像一条指令的地址,也即将要执行的指令代码),在执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计

10、方法区

方法区:Method Area

方法区是被所有线程共享,所有字段和方法字节码,以及一些特殊方法,如构造函数,接口代码也在此定义,简单说,所有定义的方法的信息都保存在该区域,此区域属于共享区间
方法区与Java堆一样,是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然Java 虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-Heap(非堆),目的应该是与Java 堆区分开来。

1. 方法区中有啥?

    静态变量(static)
    常量(final)
    类信息(构造方法、接口定义)
    运行时的常量池

2. 创建对象内存分析
还怕面试官问JVM吗?
还怕面试官问JVM吗?

创建一个对象时,方法区中会生成对应类的抽象模板;还有对应的常量池、静态变量、类信息、常量
我们通过类模板去new对象的时候

堆中存放实例对象
栈中存放对象的引用,每个对象对应一个地址指向堆中相同地址的实例对象

例如这个例子中,生成了对应的Person模板类,name常量“zsr”放在常量池中,三个对象的引用放在栈中,该引用指向放在堆中的三个实例对象。

这就是堆、栈、方法区的交互关系

11、栈

又称栈内存,主管程序的运行,生命周期和线程同步,线程结束,栈内存就释放了,不存在垃圾回收

栈:先进后出
队列:先进先出(FIFO)

1、栈中存放啥?

    8大基本类型
    对象引用
    实例的方法

2、栈运行原理

栈表示Java方法执行的内存模型
每调用一个方法就会为每个方法生成一个栈帧(Stack Frame),每个方法被调用和完成的过程,都对应一个栈帧从虚拟机栈上入栈和出栈的过程。
程序正在执行的方法一定在栈的顶部
还怕面试官问JVM吗?

3、堆栈溢出StackOverflowError

举个例子:

public class Test {
public static void main(String[] args) {
new Test().a();
}

public void a() {
b();
}

public void b() {
a();
}
}

最开始,main()方法压入栈中,然后执行a(),a()压入栈中;再调用b(),b()压入栈中;以此往复,a与b方法不断被压入栈中,最终导致栈溢出
还怕面试官问JVM吗?
还怕面试官问JVM吗?

12、堆

Heap,一个JVM只有一个堆内存(栈是线程级的),堆内存的大小是可以调节的

还怕面试官问JVM吗?

1、堆中有啥?

实例化的对象

2、堆内存详解

还怕面试官问JVM吗?

1、Young 年轻代

对象诞生、成长甚至死亡的区

Eden Space(伊甸园区):所有的对象都是在此new出来的
Survivor Space(幸存区)

幸存0区From Space)(动态的,From和To会互相交换)
幸存1区To Space

Eden区占大容量,Survivor两个区占小容量,默认比例是8:1:1

2、Tenured 老年代
3、Perm 元空间

存储的是Java运行时的一些环境或类信息,这个区域不存在垃圾回收!关闭虚拟机就会释放这个区域内存!

这个区域常驻内存,用来存放JDK自身携带的Class对象、Interface元数据。

名称演变

jdk1.6之前:永久代
jdk1.7:永久代慢慢退化,去永久代
jdk1.8之后:永久代改名为元空间

注意:元空间在逻辑上存在,在物理上不存在

新生代 + 老年代的内存空间 = JVM分配的总内存

如图所示:

还怕面试官问JVM吗?

3、什么是OOM?

内存溢出java.lang.OutOfMemoryError

产生原因:

    分配的太少
    用的太多
    用完没释放

4、GC垃圾回收

GC垃圾回收,主要在年轻代和老年代

首先,对象出生再伊甸园区

假设伊甸园区只能存一定数量的对象,则每当存满时就会触发一次轻GC(Minor GC)
轻GC清理后,有的对象可能还存在引用,就活下来了,活下来的对象就进入幸存区;有的对象没用了,就被GC清理掉了;每次轻GC都会使得伊甸园区为空
如果幸存区伊甸园都满了,则会进入老年代,如果老年代满了,就会触发一次重GC(FullGC)年轻代+老年代的对象都会清理一次,活下的对象就进入老年代
如果新生代老年代都满了,则OOM

Minor GC:伊甸园区满时触发;从年轻代回收内存

Full GC:老年代满时触发;清理整个堆空间,包含年轻代和老年代

Major GC:清理老年代

什么情况永久区会崩?

一个启动类加载了大量的第三方Jar包,Tomcat部署了过多应用,或者大量动态生成的反射类

这些东西不断的被加载,直到内存满,就会出现OOM

14、堆内存调优

1、查看并设置JVM堆内存

查看我们jvm的堆内存

public class Test {
public static void main(String[] args) {
//返回jvm试图使用的最大内存
long max = Runtime.getRuntime().maxMemory();
//返回jvm的初始化内存
long total = Runtime.getRuntime().totalMemory();
//默认情况下:分配的总内存为电脑内存的1/4,初始化内存为电脑内存的1/64
System.out.println("max=" + max / (double) 1024 / 1024 / 1024 + "G");
System.out.println("total=" + total / (double) 1024 / 1024 / 1024 + "G");
}
}

还怕面试官问JVM吗?

还怕面试官问JVM吗?
默认情况下:

JVM最大分配内存为电脑内存的1/4
JVM初始化内存为电脑内存的1/64

我们可以手动调堆内存大小

还怕面试官问JVM吗?
VM options中可以指定jvm试图使用的最大内存jvm初始化内存大小

-Xms1024m -Xmx1024m -Xlog:gc*

-Xmx用来设置jvm试图使用的最大内存,默认为1/4
-Xms用来设置jvm初始化内存,默认为1/64
-Xlog:gc*用来打印GC垃圾回收信息
还怕面试官问JVM吗?
还怕面试官问JVM吗?

2、怎么排除OOM错误?
1. 尝试扩大堆内存看结果

利用上述方法指定jvm试图使用的最大内存jvm初始化内存大小

2. 利用内存快照工具JProfiler

内存快照工具:

MAT(Eclipse)
JProfiler

作用:

分析Dump内存文件,快速定位内存泄漏
获得堆中的文件
获得大的对象

3. 什么是Dump文件?如何分析?

Dump文件是进程内存镜像,可以把程序的执行状态通过调试器保存到dump文件中

举个例子

import java.util.ArrayList;

public class Test {
byte[] array = new byte[1024 * 1024];//1M

public static void main(String[] args) {
ArrayList<Test> list = new ArrayList<>();
int count = 0;
try {
while (true) {
list.add(new Test());
count++;
}
} catch (Exception e) {
System.out.println("count=" + count);
e.printStackTrace();
}
}
}

运行该程序,报错OOM

还怕面试官问JVM吗?

接下来我们设置以下堆内存,并附加生成对应的dump文件的指令

-Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError

-XX:+HeapDumpOnOutOfMemoryError表示当JVM发生OOM时,自动生成DUMP文件。

再次点击运行,下载了对应的Dump文件

还怕面试官问JVM吗?
我们右键该类,点击Show in Explorer
还怕面试官问JVM吗?

一直点击上级目录,直到找到.hprof文件,与src同级目录下

还怕面试官问JVM吗?

我们双击打开,可以看到每块所占的大小,便于分析问题
还怕面试官问JVM吗?

点击Thread Dump,里面是所有的线程,点击对应的线程可以看到相应的错误,反馈到具体的行,便于排错

还怕面试官问JVM吗?
每次打开Dump文件查看完后,建议删除,可以在idea中看到,打开文件后生成了很多内容,占内存,建议删除
还怕面试官问JVM吗?

附:安装Jprofiler教程

    idea中安装插件
    还怕面试官问JVM吗?

    下载客户端 https://www.ej-technologies.com/download/jprofiler/files
    还怕面试官问JVM吗?

    安装客户端

    选择自定义安装,注意:路径不能有中文和空格
    还怕面试官问JVM吗?
    还怕面试官问JVM吗?
    这里name和Company任意,License Key大家可以寻找对应版本的注册机获得
    还怕面试官问JVM吗?
    还怕面试官问JVM吗?

    后续默认,安装成功即可!!!

    安装完成后,重启IDEA,可以看到我们的内存快照工具

还怕面试官问JVM吗?

​ 打开IDEA的设置,找到Tools里面的JProfiler,没有设置位置则设置位置

还怕面试官问JVM吗?
此时则全部安装完成!

15、GC垃圾回收

1、回顾

Garbage Collection:垃圾回收
还怕面试官问JVM吗?

在12.4中,我们已经对GC的流程进行了大概的讲解,这里做一些总结:

JVM在进行GC时,并不是对年轻代老年代统一回收;大部分时候,回收都是在年轻代

GC分为两种:

轻GC(清理年轻代)
重GC(清理年轻代+老年代)

2、GC算法
1、引用计数算法(很少使用)

每个对象在创建的时候,就给这个对象绑定一个计数器。

每当有一个引用指向该对象时,计数器加一;每当有一个指向它的引用被删除时,计数器减一。

这样,当没有引用指向该对象时,该对象死亡,计数器为0,这时就应该对这个对象进行垃圾回收操作。

还怕面试官问JVM吗?

2、复制算法

复制算法主要发生在年轻代幸存0区幸存1区

当Eden区满的时候,会触发轻GC,每触发一次,活的对象就被转移到幸存区,死的就被GC清理掉了,所以每触发轻GC时,Eden区就会清空;
对象被转移到了幸存区,幸存区又分为From SpaceTo Space,这两块区域是动态交换的,谁是空的谁就是To Space,然后From Space就会把全部对象转移到To Space去;
那如果两块区域都不为空呢?这就用到了复制算法,其中一个区域会将存活的对象转移到令一个区域去,然后将自己区域的内存空间清空,这样该区域为空,又成为了To Space
所以每次触发轻GC后,Eden区清空,同时To区也清空了,所有的对象都在From区

这也就是幸存0区幸存1区总有一块为空的原因

还怕面试官问JVM吗?

好处:没有内存的碎片(内存集中在一块)

坏处:

    浪费了内存空间(浪费了幸存区一半空间)
    对象存活率较高的场景下(比如老年代那样的环境),需要复制的东西太多,效率会下降。

最佳使用环境:对象存活度较低的时候,也就是年轻代

3、标记–清除算法

为每个对象存储一个标记位,记录对象的生存状态

    标记阶段:这个阶段内,为每个对象更新标记位,检查对象是否死亡;
    清除阶段:该阶段对死亡的对象进行清除,执行 GC 操作。
    还怕面试官问JVM吗?

缺点:两次扫描严重浪费时间,会产生内存碎片

优点:不需要额外的空间

4、标记–整理算法

标记-整理法标记-清除法 的一个改进版。

又叫做 标记-清楚-压缩法

    标记阶段,该算法也将所有对象标记为存活和死亡两种状态;

    不同的是,在第二个阶段,该算法并没有直接对死亡的对象进行清理,而是将所有存活的对象整理一下,放到另一处空间,然后把剩下的所有对象全部清除。
    还怕面试官问JVM吗?

可以进一步优化,在内存碎片不太多的情况下,就继续标记清除,到达一定量的时候再压缩

总结

内存(时间复杂度)效率:复制算法 > 标记清除算法 > 标记压缩算法

内存整齐度:复制算法 = 标记压缩法 > 标记清除法

内存利用率:标记压缩法 = 标记清除法 > 复制算法

思考:有没有最优的算法?

没有最优的算法,只有最合适的算法

GC 也称为 分代收集算法

对于年轻代:

对象存活率低
用复制算法

对于老年代:

区域大,对象存活率高
标记清除+标记压缩混合实现

原创:https://www.panoramacn.com
源码网提供WordPress源码,帝国CMS源码discuz源码,微信小程序,小说源码,杰奇源码,thinkphp源码,ecshop模板源码,微擎模板源码,dede源码,织梦源码等。

专业搭建小说网站,小说程序,杰奇系列,微信小说系列,app系列小说

还怕面试官问JVM吗?

免责声明,若由于商用引起版权纠纷,一切责任均由使用者承担。

您必须遵守我们的协议,如果您下载了该资源行为将被视为对《免责声明》全部内容的认可-> 联系客服 投诉资源
www.panoramacn.com资源全部来自互联网收集,仅供用于学习和交流,请勿用于商业用途。如有侵权、不妥之处,请联系站长并出示版权证明以便删除。 敬请谅解! 侵权删帖/违法举报/投稿等事物联系邮箱:2640602276@qq.com
未经允许不得转载:书荒源码源码网每日更新网站源码模板! » 还怕面试官问JVM吗?
关注我们小说电影免费看
关注我们,获取更多的全网素材资源,有趣有料!
120000+人已关注
分享到:
赞(0) 打赏

评论抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

您的打赏就是我分享的动力!

支付宝扫一扫打赏

微信扫一扫打赏