当前位置 : 主页 > 网络编程 > 其它编程 >

面试官_学完了这篇JVM,面试官真拿我没办法了!

来源:互联网 收集:自由互联 发布时间:2023-07-02
篇首语:本文由编程笔记#自由互联小编为大家整理,主要介绍了学完了这篇JVM,面试官真拿我没办法了!相关的知识,希望对你有一定的参考价值。 篇首语:本文由编程笔记#自由互联
篇首语:本文由编程笔记#自由互联小编为大家整理,主要介绍了学完了这篇JVM,面试官真拿我没办法了!相关的知识,希望对你有一定的参考价值。

篇首语:本文由编程笔记#自由互联小编为大家整理,主要介绍了学完了这篇JVM,面试官真拿我没办法了!相关的知识,希望对你有一定的参考价值。

在我们面试中经常会遇到面试官问一些有关JVM的问题下面我大概从运行时数据域、类加载机制、类加载器、垃圾收集器、垃圾收集算法、JVM堆内存模型、JVM内存结构、JVM调优等几个方面来讲一下JVM。

一、运行时数据区域

在执行Java程序的时候JAVA虚拟机会将自己所管理的内存划分为若干个不同的数据区域每个区域分工不同这些区域统称为“运行时数据区域”。下面来根据一张图来看一下这几个区域。 file 1、程序计数器

1>较小的内存空间。

2>当前线程字节码的行号指示器。

3>改变计数器的值来选取下一条需要执行的字节码指令。

4>一个处理器只会执行一条线程中的指令为了线程切换后能恢复到正确的执行位置每个线程一个计数器。

2、Java虚拟机栈

1>线程私有。

2>生命周期与线程相同。

3>每个方法执行都会创建栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。

3、本地方法栈

1>线程私有。

2>使用到的本地Native方法服务。

4、Java堆

1>内存中最大的一块。

2>线程共享。

3>堆满了抛出OutOfMemoryError异常。

5、方法区

1>线程共享。

2>用于存储已被虚拟机加载的类型信息、常量、静态变量、及时编译器编译后的代码缓存等数据。

6、运行时常量池次区域在方法区内部

1>运行时常量池是方法区的一部分。

2>存放各种字面量与符号引用。

二、垃圾收集算法

1、标记-清除算法

首先标记出所有需要回收的对象在标记完成后统一回收掉所有被标记的对象。如图。

file

2、标记-复制算法

将可用内存按容量划分为大小相等的两块每次只使用其中的一块。当这一块的内存用完了就将还存活的对象复制到另一块上面然后再把已使用过的内存空间一次清理掉。如图。

file

3、标记-整理算法

标记-整理算法就是标记-清除算法后再将存活对象整理到一起从而腾出来更大的连续空间。如图。

file

三、垃圾收集器

1、Serial收集器

file

Serial收集器是最基础、历史最悠久的收集器这个收集器是一个单线程工作的收集器。

2、ParNew收集器

file

ParNew收集器实质上是Serial收集器的多线程并行版本可以同时使用多条线程进行垃圾收集。

3、Parallel Scavenge 收集器

Parallel Scavenge收集器是一款新生代收集器它是基于标记-复制算法实现的收集器。Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量所谓吞吐量就是处理器用于运行用户代码的时间与处理器总消耗时间的比值即。

file

4、Serial Old收集器

file

Serial Old是Serial收集器的老年代版本它同样是一个单线程收集器。

5、Parallel Old收集器

Parallel Old收集器是Parallel Scavenge收集器的老年代版本支持多线程并行收集基于标记-整理算法实现。

file

6、CMS收集器

file

CMS收集器是一种以获取最短回收停顿时间为目标的收集器它的运作过程分为四个步骤包括

1> 初始标记

2> 并发标记

3> 重新标记

4> 并发清除

初始标记初始标记需要stw初始标记仅仅只是标记一下GC Roots能直接关联到的对象速度很快。

并发标记并发标记阶段就是从GC Roots的直接关联对象开始遍历整个对象图的过程这个过程耗时较长但是不需要停顿用户线程可以与垃圾收集线程一起并发运行。

重新标记重新标记阶段则是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录这个阶段的停顿时间通常会比初始标记阶段稍长一些但也远比并发标记阶段的时间短。

并发清除这个阶段清理删除掉标记阶段判断的已经死亡的对象由于不需要移动存活对象所以这个阶段也是可以与用户线程同时并发的。

6、Garbage First 收集器简称G1收集器

G1不再坚持固定大小以及固定数量的分代区域划分而是把连续的Java堆划分为多个大小相等的独立区域Region每一个Region都可以根据需要扮演新生代的Eden空间、Survivor空间或者老年代空间。如图。

file

G1收集器的运作过程大致可以划分为以下四个步骤

初始标记仅仅只是标记一下GC Roots能直接关联到的对象

并发标记从GC Root开始对堆中对象进行可达性分析递归扫描整个堆里的对象图找出要回收的对象这阶段耗时较长但可与用户程序并发执行。

最终标记对用户线程做另一个短暂的暂停用于处理并发阶段结束后仍遗留下来的最后那少量的SATB记录。

筛选回收负责更新Region的统计数据对各个Region的回收价值和成本进行排序根据用户所期望的停顿时间来制定回收计划可以自由选择任意多个Region构成回收集然后把决定回收的那一部分Region的存活对象复制到空的Region中再清理掉整个旧Region的全部空间。这里的操作设计存活对象的移动是必须暂停用户线程由多条收集器线程并行完成的。

file

四、类加载机制

当我们运行一个类的时候我们会通过java.exe来调用jvm.dll文件创建一个类加载器然后再加载我们的类我们的类在加载的过程中会经历加载、验证、准备、解析、初始化。

file

加载此阶段将类的二进制字节流加载到内存中。

验证此阶段将验证文件的格式、元数据验证、字节码验证、符号应用验证保证这些代码运行后不会危害虚拟机自身的安全。

准备准备阶段是为类中定义的变量即静态变量被static修饰的变量分配内存并设置类变量初始默认值。

解析解析阶段是Java虚拟机将常量池内的符号引用替换为直接引用的过程符号引用是用符号来描述所引用的目标而直接引用是用地址来描述所应用的目标。

初始化由于之前准备阶段是给静态变量赋值默认值而初始化阶段是给静态变量赋值他的真实的值。

五、类加载器

1、启动类加载器这个类加载器负责加载存放在 \\lib目录中的系统的jar。

2、扩展类加载器这个类加载器负责加载存放在 \\lib\\ext目录中的扩展类jar。

3、应用程序类加载器这个类加载器负责加载我们自己写的类。

六、双亲委派模型

file

前面我们说到每个类会有一个对应的类加载器去加载这个类而不同的类加载器所加载的类的类型不同当我们要加载一个类的时候首先会去应用程序类加载器加载过的集合里注这里是加载过的集合里查看有没有加载这个类如果没有就去扩展类加载器加载过的集合里查看有没有加载过这个类如果没有就再向上去找引导类加载器加载过的集合里看有没有加载过这个类如果也没有就会从引导类加载器要加载的核心类中寻找有没有要加载的类如果没有就向下寻找扩展类加载器中要加载的扩展类中有没有如果也没有就去应用程序加载器中寻找。

好处

1、不会重复去加载一个类假如应用程序类加载器加载过Student类那么下载再加载这个类的时候只需要判断应用程序类加载过的集合里有没有加载过这个类如果加载过就不用再次加载了。

2、防止恶意修改核心类库比如我们自己写一个String类我们去运行这个类系统会在引导类加载器中加载Java的核心类库中的String类而不会加载我们自定义个的String类这就防止我们私自篡改核心类库。

七、JVM堆内存模型

file

在JVM中我们的堆内存模型大概为图所示我们新生成的对象会放到eden区当我们eden区域放满了我们会进行一个轻GC会把eden区域和S0区域的存活对象放到S1区域然后将eden区域和S0区域清空然后新生成的对象接着放到eden区域当eden区域再次满了会将eden区域和S1区域的存活对象放到S0区域然后将eden区域和S1区域清空如此循环往复没进行一次循环会将没有被清除的对象年龄1如果存活的对象年龄到达15这个数值可以调整就会将此对象放到老年代或者是survivor区域满了也会将一些对象放到老年代如果当老年代满了就会进行一次重GC。

八、JVM调优

所谓JVM调优主要就是减少堆内存中重GC的次数这样我们就根据对象什么情况会进入老年代来进行分析首先是存活15代这个数值可以调整这种情况一般就是一些常量等这些一般不会有再大的优化空间或者一些代码的问题造成循环调用这种情况通过修改调整代码即可。其次是当survivor区域满了的话就会向老年代放入一些对象这个时候我们就需要根据业务情况来调整堆内存的大小以及年轻代和老年代的比例一般我们例如一些电商系统一些订单对象进入内存中就是朝生夕是订单从创建到持久化数据库中就不在内存中使用了所以一些正常的业务产生的对象会在轻GC的时候被收回但是有一些大对象可能放不下年轻代中所以就会放入老年代这种情况我们就要调大堆内存中年轻代的大小使得一些大对象可以放入到年轻代中并随着轻GC被垃圾收集器收回。

网友评论