J V M 篇

目录

 1、Java类加载器

2、双亲委托模型

从父类加载器到子类加载器分别为:

双亲委托模型的一个过程:

优点:

3、GC如何判断对象可以被回收

引用计数法:

可达性分析法:

GC Roots的对象有:

4、JVM运行时数据区(内存结构)

线程私有区:

虚拟机栈:

本地方法栈:

程序计数器:

线程共享区:

堆内存:

方法区:

5、什么情况下会内存溢出? 

堆内存溢出:

栈溢出:

6、JVM有哪些垃圾回收算法?

标记清除算法:

复制算法:

标记整理算法:

7、典型垃圾回收器

CMS:

G1 :

8、JVM中有哪些引用?

强引用:

软引用:

弱引用:

虚引用:

9、类加载过程

(1)加载 :

(2)连接:

(3)初始化 :

10、JVM类初始化顺序

11、对象的创建过程

12、对象头中有哪些信息

13、JVM内存参数

14、GC的回收机制和原理


 1、Java类加载器

  • 在JDK中,它自带了三个类加载器分别是:BootStrapClassLoader(启动类加载器)、ExtClassLoader(扩展类加载器)、AppClassLoader(应用类加载器)。
  • BootStrapClassLoader(启动类加载器):它是ExtClassLoader的父类加载器,默认的加载方式是在%JAVA_HOME%lib目录下的jar包和class文件。
  • ExtClassLoader(扩展类加载器):它是AppClassLoader的父类加载器,它负责加载%JAVA_HOME%lib/ext文件夹下的jar包和class类。
  • AppClassLoader(应用类加载器):它是自定义加载器的父类,负责加载classpath下的类文件。系统类加载器,线程上下文加载器。
  • 它们全部都继承了ClassLoader并且实现了自定义类加载器。


2、双亲委托模型

从父类加载器到子类加载器分别为:

 

  • BootStrapClassLoader    加载路径为:JAVA_HOME/jre/lib
  • ExtensionClassLoader    加载路径为:JAVA_HOME/jre/lib/ext
  • ApplicationClassLoader  加载路径为:classpath

还有一个自定义类加载器

  1. 当一个类加载器收到类加载请求时,会先把这个请求交给父类加载器处理,
  2. 若父类加载器找不到该类,再由自己去寻找。
  3. 该机制可以避免类被重复加载,还可以避免系统级别的类被篡改

双亲委托模型的一个过程:

        如果一个类加载器收到了类加载器的请求,首先它不会自动得去尝试加载这个类它会把这个类托付给父类加载器去完成,每一层依次都是这样。因此所有的加载请求都会被传送到顶层的启动类加载器(BootStrapClassLoader)中。只有当父类加载器反馈自己无法完成加载请求的时候,其实就是找不到类的时候,这个时候子类加载器才会尝试自己去加载,这个过程就叫做双清委托模型

J V M 篇_第1张图片

J V M 篇_第2张图片

优点:

  • 主要是为了安全,避免了用户自己编写的类动态替换了Java的一些核心类,比如String
  • 同时也避免了类的重复加载因为在JVM中区分不同类,不单是根据类名,在相同的class文件中被不同的ClassLoader加载就是不同的两个类

 


3、GC如何判断对象可以被回收

引用计数法:

 

  • 其实就是每个对象都有一个引用计数属性,新增一个引用时计数的时候会加1,引用释放时计数的时候会减1,计数为0的时候就可以回收

 

其实简单点就是已淘汰,为每个对象添加引用计数器,引用为0时判定可以回收,会有两个对象相互引用无法回收的问题

可达性分析法:

 

  • 从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。
  • 当一个对象到 GC Roots 没有任何引用链相连的时候,就可以证明此对象是不可用的,那么虚拟机就判断是可回收对象。

其实简单点就是从GCRoot开始往下搜索,搜索过的路径称为引用链,若一个对象GCRoot没有任何的引用链,则判定可以回收

引用计数法,可能会出现A 引用了 B,B 又引用了 A,这时候就算它们都不再使用了,但因为相互引用计数器=1,也是永远无法被回收。

GC Roots的对象有:

 

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中JNI(其实就是一般说的Native方法)引用的对象

 

简单点就是虚拟机栈中引用的对象,方法区中静态变量引用的对象,本地方法栈中引用的对象(好理解!)

可达性算法中的不可达对象并不是立即死亡的,对象拥有一次自我拯救的机会。

对象被系统宣告死亡至少还要经历两次标记过程:

  • 第一次是经过可达性分析发现没有与GC Roots相连接的引用链,
  • 第二次是在由虚拟机自动建立的Finalizer队列中判断是否需要执行finalize()方法。

  • 当对象变成(GC Roots)不可达的时候,GC会判断该对象是否覆盖了finalize方法,
  • 如果没有覆盖,那么就直接将其回收。
  • 如果覆盖了,若对象未执行过finalize方法,将其放入F-Queue队列,由一低优先级线程执行该队列中对象的finalize方法。
  • 执行finalize方法完毕后,GC会再次判断该对象是否可达,
  • 若不可达的话,就会进行回收,
  • 如果可达的话,对象就会“复活”

  • 每个对象只能触发一次finalize()方法
  • 由于finalize()方法运行代价高昂,不确定性大,无法保证各个对象的调用顺序,不推荐大家使用,建议遗忘它。


4、JVM运行时数据区(内存结构)

线程私有区:

虚拟机栈:

 

  • 每次调用方法都会在虚拟机栈中产生一个栈帧,
  • 每个栈帧中都有方法的参数、局部变量、方法出口等信息,方法执行完毕后释放栈帧

本地方法栈:

 

  • 它为native修饰的本地方法提供的空间,在HotSpot中与虚拟机合二为一

程序计数器:

 

  • 保存指令执行的地址,方便线程切回后能继续执行代码

线程共享区:

堆内存:

 

  • Jvm进行垃圾回收的主要区域,存放对象信息,分为新生代和老年代,内存比例为1:2,
  • 新生代的Eden区内存不够时时发生MinorGC,老年代内存不够时发生FullGC

 

方法区:

 

  • 存放类信息、静态变量、常量、运行时常量池等信息。
  • JDK1.8之前用持久代实现,JDK1.8后用元空间实现,元空间使用的是本地内存,而非在JVM内存结构中


5、什么情况下会内存溢出? 

堆内存溢出:

 

  1. 当对象一直创建而不被回收时
  2. 加载的类越来越多时
  3. 虚拟机栈的线程越来越多时

栈溢出:

 

  • 方法调用次数过多,一般是递归不当造成


6、JVM有哪些垃圾回收算法?

 

标记清除算法:

 

  • 标记不需要回收的对象,然后清除没有标记的对象,会造成许多内存碎片。

 

复制算法:

 

  • 将内存分为两块,只使用一块,进行垃圾回收时,先将存活的对象复制到另一块区域,然后清空之前的区域。(用在新生代)

 

标记整理算法:

 

  • 与标记清除算法类似,但是在标记之后,将存活对象向一端移动,然后清除边界外的垃圾对象。(用在老年代)


 

7、典型垃圾回收器

CMS:

 

  • 以最小的停顿时间为目标、只运行在老年代的垃圾回收器,使用标记-清除算法,可以并发收集。

G1 :

 

  • JDK1.9以后的默认垃圾回收器,注重响应速度,支持并发,
  • 采用标记整理+复制算法回收内存,使用可达性分析法来判断对象是否可以被回收。


8、JVM中有哪些引用?

强引用:

 

  • new的对象。哪怕内存溢出也不会回收

软引用:

  • 只有内存不足时才会回收

弱引用:

 

  • 每次垃圾回收都会回收

虚引用:

 

  • 必须配合引用队列使用,一般用于追踪垃圾回收动作


9、类加载过程

(1)加载 :

 

  • 把字节码通过二进制的方式转化到方法区中的运行数据区

 

(2)连接:

 

 验证:

  • 验证字节码文件的正确性

 准备:

  • 正式为类变量在方法区中分配内存,并设置初始值,final类型的变量在编译时已经赋值了

 解析:

  • 将常量池中的符号引用(如类的全限定名)解析为直接引用(类在实际内存中的地址) 

 

(3)初始化 :

 

  • 执行类构造器(不是常规的构造方法),为静态变量赋初值并初始化静态代码块。


10、JVM类初始化顺序


父类静态代码块和静态成员变量 - > 子类静态代码块和静态成员变量 - > 父类代码块和普通成员变量 - > 父类构造方法 - > 子类代码块和普成员变量 - > 子类构造方法


11、对象的创建过程

  1. 检查类是否已被加载,没有加载就先加载类
  2. 为对象在堆中分配内存,使用CAS方式分配,防止在为A分配内存时,执行当前地址的指针还没有来得及修改,对象B就拿来分配内存。
  3. 初始化,将对象中的属性都分配0值或null
  4. 设置对象头
  5. 为属性赋值和执行构造方法


12、对象头中有哪些信息

  • 对象头中有两部分,
  • 一部分是MarkWork,存储对象运行时的数据,如对象的hashcode、GC分代年龄、GC标记、锁的状态、获取到锁的线程ID等;
  • 另外一部分是表明对象所属类,如果是数组,还有一个部分存放数组长度


13、JVM内存参数

  • -Xmx[]:堆空间最大内存
  • -Xms[]:堆空间最小内存,一般设置成跟堆空间最大内存一样的
  • -Xmn[]:新生代的最大内存

  • -xx:[survivorRatio=3]:eden区与from+to区的比例为3:1,默认为4:1
  • -xx[use 垃圾回收器名称]:指定垃圾回收器
  • -xss:设置单个线程栈大小

一般设堆空间为最大可用物理地址的百分之80


14、GC的回收机制和原理

  • GC的目的实现内存的自动释放,使用可达性分析法判断对象是否可回收,
  • 采用了分代回收思想,将堆分为新生代、老年代,新生代中采用复制算法,老年代采用整理算法,
  • 当新生代内存不足时会发生minorGC,老年代不足时会发送fullGC
     

你可能感兴趣的:(面试题,jvm,java,开发语言)