JVM知识回顾

2020-03-11  本文已影响0人  洛美萨斯

JVM知识回顾

1. 简述JDK,JRE和JVM之间的关系

jdk架构

2. 类加载机制的作用和过程

一句话解释:类的加载指的是将.class文件中的二进制数读入到内存中,将其放在运行数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。
类的生命周期

类的加载过程包括加载、验证、准备、解析、初始化五个阶段

1)加载

​ 在加载阶段,虚拟机主要做了三件事

  1. 通过一个类的全限定类名来获取其定义的二进制字节流
  2. 将这个字节流代表的静态存储结构转化为方法区的运行时数据结构
  3. 在堆中生成一个代表这个类的Class对象,作为方法区中这些数据的访问入口

2)验证

​ 保证被加载类的准确性

  1. 文件格式的验证:验证.class文件字节流是否符合class文件的格式规范,并且能够被当前版本的虚拟机处理。这里主要对魔数、主版本号、常量池等等的校验
  2. 元数据验证:主要对字节码描述的信息进行语义分析,以保证其描述的信息符合java语言规范的要求。比如说验证这个类是不是有父类,类中的字段方法是不是和父类有冲突
  3. 字节码验证:这是整个验证过程中最复杂的阶段,主要是通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。在元数据验证阶段对数据类型做出验证后,这个阶段主要对类的方法做出分析,保证类的方法在运行时不会做出危害虚拟机的事。
  4. 符号引用验证:他是验证的最后一个阶段,发生在虚拟机将符号引用转化为直接引用的时候。主要是对类自身以外的信息进行校验。目的是确保解析动作能够完成。

3)准备

​ 准备阶段主要为类变量分配内存并设置初始值

  1. 类变量(static)会分配内存,但实例变量不会,实例变量主要随着对象的实例化一块分配到java堆中

  2. 这里的初始值值得是数据类型默认值,不是代码中被显示赋予的值。比如

    public static int a = 1;
    

    这里的a在准备阶段过后值为0,而不是1。赋值为1的动作在初始化阶段。

4)解析

​ 解析阶段主要是虚拟机将常量池中的符号引用转为直接引用的过程

5)初始化

​ 主要为类的静态变量赋予正确的初始值,有两种方式

  1. 声明类变量时指定初始值
  2. 使用静态代码块为类变量指定初始值

3. 运行时数据区分为哪几块?说说你对每块区域的理解(某些存储的内容,生命周期,作用等)

运行时数据区

​ 运行时数据区分为方法区、堆、虚拟机栈、本地方法栈、程序计数器

  1. 程序计数器:线程私有,可以看成是当前线程所执行的字节码的行号指示器,用于选取下一条需要执行的指令,也是唯一一个没有OOM情况的区域,与线程共存亡
  2. Java虚拟机栈:线程私有,为JVM执行Java方法服务,主要描述Java方法执行的线程内存模型,与线程共存亡
  3. 本地方法栈:线程私有,与虚拟机栈相似,为JVM使用到的Native方法服务,Native方法多用C++写
  4. Java堆:线程共享,主要用于存储实例对象,Java中几乎所有的对象实例都会存储到这块内存中,考虑到即时编译和逃逸分析,有一部分的对象实例会分配到栈上
  5. 方法区:线程共享,主要用于存储被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码缓存等数据
  6. 运行时常量池:线程共享,属于方法区的一部分,用于存放编译期生成的各种字面量和符号引用

4. 结合Eden,S0,S1和Old区,描述一下一个对象创建的过程

​ 对象的分配,从理论上来讲应该都是在堆上分配(即时编译和逃逸分析,会分配到栈上)。在正常分代的情况中,新生成的对象会分配在新生代中,但是少数情况下(比如对象大小超过一定阈值)会直接分配到老年代。

​ 大多数情况下,对象在Eden区中分配,当Eden区没有足够空间时,虚拟机会发起一次Minor GC,新生代采用的是复制算法,把存活的对象从Eden和S0复制到S1区,后把对象年龄加一,并清空Eden和S0(新生代的对象大多为活跃的对象,会频繁的创建和销毁,所以新生代GC时应该存活的对象很少,所以采用复制算法是最合适的)。当对象达到一定年龄之后(默认15岁,可修改-XX:MaxTenuringThresold=需要的年龄数字),对象会被移动到老年代,当然老年代如果空间不足,会发生Major GC,采用标记整理算法(老年代的对象大部分都是经过一段Minor GC的,所以相对比较稳定,每次回收的对象相对比较少,所以采用标记整理算法最合适)。
​ 另外,一个对象的创建过程,上述描述只是一个对象的创建过程中的一个步骤中的详解,一个对象的创建,从虚拟机的角度来看,包括:遇到new指令—判断引用能否在常量池定位;检查引用能否被加载、解析、初始化过—分配内存(包括指针碰撞和空闲列表法,这一部分内容是我上面的描述)—初始化内存空间—设置对象头—执行init()方法。

5. 怎样确定一个对象为垃圾

  1. 引用计数法

    ​ 引用计数算法就是在对象中添加了一个引用计数器,当有地方引用这个对象时,引用计数器的值就加1,当引用失效的时候,引用计数器的值就减1。当引用计数器的值为0时,jvm就开始回收这个对象。

    简单的来说,在JVM中的栈中,如果栈帧中指向了一个对象,那么堆中的引用计数器的值就会加1,当栈帧这个指向null时,对象的引用计数器就减1。

    ​ 这种方法虽然很简单、高效,但是JVM一般不会选择这个方法,因为这个方法会出现一个问题:当对象之间相互指向时,两个对象的引用计数器的值都会加1,而由于两个对象时相互指向,所以引用不会失效,这样JVM就无法回收。

  2. 可达性分析法(Reachability Analysis)
    所有生成的对象都是一个称为"GC Roots"的根的子树。从GC Roots开始向下搜索,搜索所经过的路径称为引用链(Reference Chain),当一个对象到“GC Roots”没有任何引用链可以到达时,就称这个对象是不可达的(不可引用的),也就是可以被GC回收了。

可达性分析法

6. 常用的垃圾回收算法有哪些?有什么优缺点

  1. **标记-清除 **

    ​ 分为两个阶段:标记和清除。根据根节点可达分析哪些对象为垃圾,并对垃圾进行标记,然后统一回收。这是最基础的算法,后续的收集算法都是基于这个算法扩展的。

    不足:

    • 扫描整个堆内存,耗时,效率低;

    • 标记清除之后会产生大量碎片。

  2. 标记整理

    ​ 分为两个阶段:标记和整理。根据根节点可达分析哪些对象为垃圾,并对垃圾进行标记,将还在被使用的对象移动到一端。然后在清理掉这个范围之外的垃圾。

    优点:

    • 避免了内存碎片

    缺点:

    • 整理内存耗时
  3. 复制

    ​ 将内存按容量划分为大小相等的两块,每次只使用其中一块,当这一块内存用完,将还在被使用的对象复制到另一块上,再将已使用过的那一块内存空间全部清理掉。

    优点:

    • 效率高,实现简单

    缺点:

    • 内存空间利用率只用50%

7. 简述你对吞吐量和停顿时间的理解

​ 在实践活动中,我们通过最优吞吐量和最短停顿时间来评价jvm系统的性能:

吞吐量越高算法越好
暂停时间越短算法越好
​ 首先让我们来明确垃圾收集(GC)中的两个术语:吞吐量(throughput)和暂停时间(pause times)。 JVM在专门的线程(GC threads)中执行GC。只要GC线程是活动的,它们将与应用程序线程(application threads)争用当前可用CPU的时钟周期。简单点来说,吞吐量是指应用程序线程用时占程序总用时的比例。例如,吞吐量99/100意味着100秒的程序执行时间应用程序线程运行了99秒,而在这一时间段内GC线程只运行了1秒。
术语”暂停时间”是指一个时间段内应用程序线程让与GC线程执行而完全暂停。例如,GC期间100毫秒的暂停时间意味着在这100毫秒期间内没有应用程序线程是活动的。 如果说一个正在运行的应用程序有100毫秒的“平均暂停时间”,那么就是说该应用程序所有的暂停时间平均长度为100毫秒。同样,100毫秒的“最大暂停时间”是指该应用程序所有的暂停时间最大不超过100毫秒。

8. 常见的jdk命令和工具有什么?并简述一下他们的作用

​ 常见的jdk命令:

上一篇 下一篇

猜你喜欢

热点阅读