其他分享
首页 > 其他分享> > JVM面试之多的是我不知道的事

JVM面试之多的是我不知道的事

作者:互联网

1:JVM内存模型

Arthas:Alibaba开源的Java诊断工具,采用命令行交互模式,提供了丰富的功能,是排查jvm相关问题的利器。
jvm中每调用一个方法就会生成一个栈帧,放在栈空间
栈区分为虚拟机栈和本地栈
虚拟机栈存储java方法的栈帧,本地栈存储native方法的栈帧
程序计数器:存放每一个线程执行到了方法的哪一步,每个线程都有程序计数器
程序计数器在jvm相当于记录指令的内存地址(指令执行到哪一步了)
本地栈是没有程序计数器的,因为本地方法是C/C++的,java无法计数,程序计数器一直是0

方法区:class类信息,静态变量,常量
在jdk1.8之前,方法区是堆内的一块连续的区域
在jdk1.8之后,方法区从jvm内存移出来了,变成元数据区(MetaSpace),移到了操作系统之中

堆:存放对象,数组
堆分为新生代,老年代。默认新生代占堆内存的1/3,老年代占堆内存的2/3。
新生代分为Eden区和两个Surrivor区(2/10),Eden区占新生代的(8/10)

2:java类加载过程,什么是双亲委派机制,有什么作用

public class classLoaderTest {
    public static void main(String[] args) throws ClassNotFoundException {
       //父子关系 AppClassLoader<-ExtClassLoader<-BootStrap ClassLoader
        ClassLoader cl1=classLoaderTest.class.getClassLoader();
        System.out.println("cl1->"+cl1);
        System.out.println("parent of cl1->"+cl1.getParent());
        //打印出null,因为BootStrap ClassLoader由C++开发,是JVM虚拟机的一部分,本身不是JAVA类
        System.out.println("grandparent of cl1->"+cl1.getParent().getParent());

        ClassLoader cl2=String.class.getClassLoader();

        //String 和 java.util.List 这两个类是JVM启动的时候就会默认加载到JVM进程中的,它们的ClassLoader打印出来都是null
        //这就说明这个null是jvm内存还空无一物的时候,由底层C++初始化的ClassLoader
        System.out.println("cl2->"+cl2);
        System.out.println(cl1.loadClass("java.util.List").getClass().getClassLoader());
    }
}

在这里插入图片描述
在这里插入图片描述
JAVA的类加载器:AppClassLoader<-ExtClassLoader<-BootStrap ClassLoader
每种类加载器都有自己的加载目录

/**
     * Loads the class with the specified <a href="#name">binary name</a>.  The
     * default implementation of this method searches for classes in the
     * following order:
     *
     * <ol>
     *
     *   <li><p> Invoke {@link #findLoadedClass(String)} to check if the class
     *   has already been loaded.  </p></li>
     *
     *   <li><p> Invoke the {@link #loadClass(String) <tt>loadClass</tt>} method
     *   on the parent class loader.  If the parent is <tt>null</tt> the class
     *   loader built-in to the virtual machine is used, instead.  </p></li>
     *
     *   <li><p> Invoke the {@link #findClass(String)} method to find the
     *   class.  </p></li>
     *
     * </ol>
     *
     * <p> If the class was found using the above steps, and the
     * <tt>resolve</tt> flag is true, this method will then invoke the {@link
     * #resolveClass(Class)} method on the resulting <tt>Class</tt> object.
     *
     * <p> Subclasses of <tt>ClassLoader</tt> are encouraged to override {@link
     * #findClass(String)}, rather than this method.  </p>
     *
     * <p> Unless overridden, this method synchronizes on the result of
     * {@link #getClassLoadingLock <tt>getClassLoadingLock</tt>} method
     * during the entire class loading process.
     *
     * @param  name
     *         The <a href="#name">binary name</a> of the class
     *
     * @param  resolve
     *         If <tt>true</tt> then resolve the class
     *
     * @return  The resulting <tt>Class</tt> object
     *
     * @throws  ClassNotFoundException
     *          If the class could not be found
     */
    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

根据上面代码解读类加载机制:
每次加载过的类都是有缓存的(通过底层C+代码做的),如果缓存中有,直接跳过if (c == null) ,如果缓存中没有,就去找parent if (parent != null),就让父类代替加载这个类,父类也会去找是否自己被加载过
parent为空,就进入下面的BootstrapClassLoader

 c = findBootstrapClassOrNull(name);

如果还是没有找到,就自己去找

在这里插入图片描述

双亲委派机制的核心如下图
要加载一个类(AppClassLoader),如果已经加载过,就直接返回,否则去找他的父类(ExtClassLoader),看父类有没有加载过,有直接返回,没有,就继续找ExtClassLoader的父类BootStrap ClassLoader,看有没有加载过,有就直接返回,没有的话,就如下图的右边部分,去BootStrap ClassLoader规定的路径去找BootStrap ClassLoader,找不到的话,就向下,去ExtClassLoader的规定加载路径找,再找不到,就交由应用程序自己加载,就去Class.path属性下去找
总结:向上委托查找,向下委托加载

在这里插入图片描述

双亲委派的作用:对java底层起保护作用,保护java底层的类不会被应用程序覆盖

比如,你自己再定义一个String类,可以写自己的main方法,如果可以这样,java就乱掉了,因为String还要进行其他的操作,就是对基础的类进行保护

如果我们自己写了一个String类,那么加载的时候,类加载器不会直接找自定义的String,而是向上找,一直找到BootStrap ClassLoader,因为BootStrap ClassLoader里面有一个String类,而不是去class,path下面找String

类加载过程:加载 ->连接 ->初始化
加载:通过双亲委派机制把class文件(字节码)加载到jvm内存之中,并映射成jvm认可的数据结构
连接 :resolveClass方法,是一个native方法

在这里插入图片描述

连接分为3个阶段:

然后初始化
类加载完执行静态代码块和构造方法

类加载

3:一个对象从加载到JVM,再到被GC清除,都经历过什么过程

类的初始化:静态属性,静态代码块是在类的加载过程中加载的
对象的初始化:普通属性,构造方法是在对象创建的时候初始化的

对象的创建:

GC是推动对象在JVM内存中移动的关键

对象一定会创建在堆区吗?

不一定,在某些情况下,为了优化,会创建在栈区,当对象只在一个地方用的时候,会优先在栈中分配,在栈里分配,生命周期就变得比较简单,方法一结束,栈帧就没了,对象就移除了,就不用GC介入了

正常情况下,对象是要在堆上进行内存分配的,但是随着编译器优化技术的成熟,虽然虚拟机规范是这样要求的,但是具体实现上还是有些差别的。
如HotSpot虚拟机引入了JIT优化之后,会对对象进行逃逸分析,如果发现某一个对象并没有逃逸到方法外部,那么就可能通过标量替换来实现栈上分配,而避免堆上分配内存。
————————————————
版权声明:本文为CSDN博主「吃饭的时候记得叫我啊」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wtmdcnm/article/details/104921377
JVM系列 - Java对象都是创建在堆内存中的吗?

4:怎么确定一个对象到底是不是垃圾,什么是GC Root?

比如拆房子,需要先把要拆的地方标记一个拆字,然后拆迁队就相当于垃圾回收器去拆

有两种定位垃圾的方式

在这里插入图片描述

根搜索算法的基本思路就是通过一系列名为”GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的

5:JVM有哪些垃圾回收算法

垃圾回收算法是组成垃圾回收器的工具

在这里插入图片描述

在这里插入图片描述

这三种算法各有利弊,各有各自的适合场景

6:JVM有哪些垃圾回收器,它们都是怎么工作的?什么是STW,它们都发生在哪些阶段 ?什么是三色标记,如何解决错标记和漏标记的问题,为什么要设计这么多的垃圾回收器?

什么是STW

Stop the world .是在垃圾回收算法执行过程中,需要将JVM内存冻结的一种状态(比如要打扫卫生,就让房间里的其他人先停止制造垃圾,等我打扫完)在STW状态下,java的所有线程都是停止执行的,GC线程除外,native方法可以执行(因为调用的是底层C++的代码,跟JVM没有太大的关系),但是这些native方法不能与JVM进行交互,GC各种算法优化的重点,就是减少STW,同时,这也是JVM调优的重点。

JVM的垃圾回收器

在这里插入图片描述Serial:串行,通知所有线程,我要垃圾回收了,开启一个GC线程,垃圾回收之后,这些线程再执行。就像踢足球,需要GC时,直接暂停,GC完了再继续
弊端:只开启了一个GC线程,效率较低,Serial是比较早期的垃圾回收器,在单CPU还可以,在多CPU架构下,性能就会下降,通常只适用于几十兆的内存空间(就像一个人打扫房子,房子大了就打扫不过来)
在这里插入图片描述
Parallel:并行,在Serial基础上,多开启几个GC线程进行垃圾回收

PS(Pprallel Scavenge)+PO(Parallel old)是jdk1.8默认的垃圾回收器
在多CPU的架构下,性能会比Serial高很多(很多人一起扫房子)

CMS(Concurrent Mark Sweep并行标记清除):我们发现,很多人一起扫,也扫不过来
CMS是很重要的垃圾回收器,促进了垃圾回收器从分代到不分代的转变过程
核心思想是将STW打散,让一部分GC线程与用户线程并发执行,整个GC过程分为4个阶段
在这里插入图片描述
1:初始标记阶段:STW只标记出根对象直接引用的对象
2:并发标记:继续标记其他对象
3:重新标记:STW对并发执行阶段的对象进行重新标记
4:并发清除:并行,将产生的垃圾清除。清除过程中应用程序又会不断产生新的垃圾,这些垃圾叫浮动垃圾,留到下一次GC过程中清除

G1:Garbage First:垃圾优先
对于这个垃圾回收器,堆内存不再分新生代和老年代,而是把堆内存分为一个一个的小块,每一个小块叫做Region。逻辑上不分代,实际上是分代的。每个Region可以隶属于不同的年代

在这里插入图片描述GC分为四个阶段
1:初始标记,标记出GC Root直接引用的对象。STW
2:标记Region,通过RSet标记出上一个阶段标记的Region引用到的Old区Region
RSet:Region中的一个小的数据结构,在每一个Region当中会有一个RSet,记录与当前Region有引用关系的Region。 是并发的,没有STW

3:并发标记阶段:跟CMS差不多,不过遍历的范围不再是整个OLD区,只需要标记第2步标记出来的Region

4:重新标记:跟CMS的重新标记差不多,方法不同。把并发标记过程中产生变化了的对象重新标记一下

5:垃圾清理:采用复制算法,直接将整个Region的信息复制到一个新的Region,这个阶段G1只选则垃圾较多的Region进行清理,而不是全部清理

ZGC和shennandoah逗孩子啊优化之中,是未来的垃圾回收器

CMS三色标记

三色标记是CMS中用来标记的机制,是CMS的核心算法
是一种逻辑上的抽象,将每个内存对象分成三种颜色,黑色表示自己和成员变量都已经标记完毕,灰色表示自己标记完了,成员变量还没有标记完,白色表示自己未标记完

错标记:

漏标记

为什么要设计这么多的垃圾回收器?

因为内存逐渐变大

7:如何进行JVM调优?JVM参数有哪些?怎么查看一个JAV进程的JVM参数

JVM调优主要就是通过定制JVM运行参数来提高JAVA应用程序的运行速度
在这里插入图片描述

推荐阅读

Java面试200道系统教程助力拿下年薪60万!并发网络通信JVM面试缓存/微服务/spring/MySQL
图解Java 垃圾回收机制
携程面试官问我怎么划分 Java 虚拟机内存区域,相见恨晚!
携程面试官竟然问我 Java 虚拟机栈!
Java虚拟机(JVM)面试题(2020最新版)
JVM相关问题整理
Java垃圾回收、引用计数法、根可达算法
java垃圾回收机制–可达性算法
常见JVM面试题及答案整理
JVM面试题总结
用最直接的大白话来聊一聊Java对象的GC垃圾回收以及Java对象在内存中的那些事
深入理解 JVM 垃圾回收机制及其实现原理
JVM架构和GC垃圾回收机制(JVM面试不用愁)
Java虚拟机(JVM)你只要看这一篇就够了!
垃圾回收和GC调优
深入理解JVM的垃圾回收机制

标签:标记,对象,面试,GC,垃圾,JVM,之多,内存
来源: https://blog.csdn.net/ningmengshuxiawo/article/details/115671006