Java虚拟机相关面试题

如果对象的引用被置为null,垃圾收集器是否会立即释放对象占用的内存?

不会,在下一个垃圾回收周期中,这个对象将是可被回收的。

也就是说当一个对象的引用变为 null 时,并不会被垃圾收集器立刻回收,而是在下一次垃圾回收时才会释放其占用的内存。

finalize()方法工作原理

一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其 finalize() 方法(如果如果覆盖了finalize()),并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。

至于为什么在下一次垃圾回收动作发生时才会回收内存,原因是如果一个对象覆盖了 finalize() 方法,那么在真正被宣告死亡的时候,至少需要经过两次标记。第一次被标记的时候会被放在 一个 F-Queue 队列中,finalize() 方法是对象逃脱死亡命运的最后一次机会。在第二次标记的时候,如果该对象成功与引用链(GC-Roots)上的任何一个对象关联,那么它仍然可以存活下来,否则将会被垃圾收集器回收。

Java 8的内存分代改进

在 jdk1.8 中对内存模型中方法区的实现永久代(Perm Gen space)进行了移除,取而代之的是元空间(Metaspace)。

原因是在方法区中实现垃圾回收的条件比较苛刻,因此存在着内存溢出的风险。在jdk1.8之后,当方法区内存使用较多时,元空间会使用物理内存,减少了风险。如下
这里写图片描述

分别写出堆内存溢出与栈内存溢出的程序

这里涉及到Java运行时数据区域方面的知识。

栈内存溢出

    public void f() {
        f();
    }

堆内存溢出

    public void testd() {
        List<String> list = new ArrayList<>();
        int i = 0;

        while (true) {
            list.add(new String(i + ""));
            i++;
        }
    }

反射中,Class.forName()ClassLoader.loadClass()区别

虽然问到的是反射,但是在底层涉及到了虚拟机的类加载知识,只有了解了虚拟机的类加载过程,才能更好地理解这个题目
这里写图片描述

  • 加载:加载 class 的二进制流,将字节流存储结构转化为方法区运行的数据结构,生成一个 Class 对象作为这个类的访问入口
  • 验证:保证 class 文件的字节流中包含的信息符合虚拟机的要求,比如文件格式验证,元数据验证等
  • 准备:为类变量分配内存,并设置初始值,并非在堆中分配内存,而是在方法区
  • 解析:将常量池中的符号引用替换为直接引用
  • 初始化:也是类加载的最后一步,执行类构造器 clinit() 方法,按照要求初始化静态变量的值,并执行静态代码块

下面是 Class.forName()ClassLoader的区别

  • Class.forName() 默认执行类加载过程中的连接与初始化动作,一旦执行初始化动作,静态变量就会被初始化为程序员设置的值,如果有静态代码块,静态代码块也会被执行
  • ClassLoader.loadClass() 默认只执行类加载过程中的加载动作,后面的动作都不会执行

比如我们连接 MySQL 数据库会用到的 Class.forName("com.mysql.cj.jdbc.Driver"),在类加载的过程中就会通过静态代码块注册一个驱动对象。

    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }

说说强引用、软引用、弱引用、虚引用以及他们之间和gc的关系

  • 强引用是指在代码中普遍存在的,类似 Object obj = new Object(); 这类的引用,只要强引用还存在,垃圾回收器永远不会回收掉引用的对象
  • 软引用是用来描述一些还有用但并非是必要的对象。对于软引用着的对象,在系统将要发生内存溢出异常之前,将会把这类对象列进回收范围进行第二次的回收。如果这次回收仍然没有足够的内存,就会抛出内存溢出异常。在jdk1.2 中提供了SoftReference 类来实现软引用
  • 弱引用也是用来描述非必须对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次的垃圾回收之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在jdk1.2 中提供了WeakReference 类来实现弱引用
  • 虚引用也被称为幽灵引用或幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间造成影响,也无法通过虚引用来取得一个对象的实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集时收到一个系统通知。在jdk1.2 中提供了PhantomReference 类来实现虚引用。

JVM垃圾回收机制,何时触发MinorGC等操作

Minor GC 也被称为新生代 GC,指发生在新生代(PSYoungGen)的垃圾收集动作,新生代包括三块内存区域 eden 区,fromFrom Survivor)区 与 toTo Survivor) 区。对象优先在 eden 创建并区分配内存,当 eden 区内存无法为一个新对象分配内存时,就会触发 Minor GC。至于为什么把新生代分为 3 个区,主要是为了新生代复制算法的实现。

对象如何晋升到老年代

对象优先在新生代的 eden 区分配内存,但是也并不绝对,下面几种情况对象会晋升到老年代

  • 大对象直接进入老年代。比如很长的字符串,或者很大的数组等
  • 长期存活的对象进入老年代。在堆中分配内存的对象,其内存布局的对象头中(Header)包含了 GC 分代年龄标记信息。如果对象在 eden 区出生,那么它的 GC 分代年龄会初始值为 1,每熬过一次 Minor GC 而不被回收,这个值就会增加 1 岁。当它的年龄到达一定的数值时(jdk1.7 默认是 15 岁),就会晋升到老年代中。
  • 动态对象年龄判定。当 Survivor 空间中相同年龄所有对象的大小总和大于 Survivor 空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,而不需要达到默认的分代年龄。

EdenSurvivor的比例分配等

这个比例在每个 jdk 版本中可能是不一样的,在 jdk1.7 中 EdenSurvivorfromto)的比例是 8 : 1。在 jdk1.8 是 6 : 1,如下
这里写图片描述

什么是类加载器的双亲委派模型

双亲委派模型是虚拟机类加载机制中有关类加载器的相关的东西。下面是类加载器的双亲委派模型
这里写图片描述
类加载器的双亲委派模型并不是一个强制性的约束模型,而是 Java 设计者推荐给开发者的一种加载器方式。上面类加载器的父子关系一般不会以继承的方式实现,而是采用组合的关系来复用父类加载器的代码。

工作过程:如果一个类加载器收到了类加载的请求,它首先不会自己去加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求都应该传送到顶层的启动类加载器,只有当父加载器无法完成这个加载请求时,子加载器才会尝试自己去加载。

为什么要有双亲委派模型呢?原因是双亲委派模型可以保证 Java 程序的稳定性。比如你有一个类,在不采用双亲委派模型的情况下,可能会有不同的加载器去加载这个类,不同类加载器加载出来的 Class 文件必然不相同,这样就造成了不一致性。

什么是指令重排序、内存屏障与先行发生原则

想必这两个名词有很多 Java 程序员都或多或少的见过,因为理解起来比较困难,这里就简单的阐述一下,并不作详细的分析

  • 指令重排序:CPU 采用了允许将多条指令不按程序规定的顺序发送给相应的电路单元处理。可以简单的理解为不按程序的顺序执行
  • 内存屏障:内存屏障是指指令重排序时,通过一个加锁指令,把锁前后指令的执行顺序分隔开,不能把后面的指令重排序到内存屏障之前的位置
  • 先行发生原则:Java 内存模型中定义的两项操作之间的偏序关系。如果 A 先行发生于 B 之前,操作 A 产生的影响会被操作 B 观察到。这些影响可以是修改了内存中共享变量的值,发送了消息,调用了方法等

Java 中的 volatile 关键字有两个主要的作用,一个是保证内存的可见性,还有一个作用就是禁止指令重排序的发生。

volatile 的语义,它修饰的变量一定线程安全吗

上面我们已经简答的提到了 volatile 关键字的作用,一个是保证内存的可见性,还有防止指令重排序。下面再来解释一下内存可见性

  • 内存可见性:当一条线程修改了某个值,这个新值对于其他的线程是立即可见的,普通的变量不具备这个特征

下面说结论,被 volatile 关键字修饰的变量不是线程安全的,因为 volatile 不能保证原子性。再另外的说一句,被 synchronized 修饰的代码块具备原子性。

后面遇到相关的题目会陆续补充

猜你喜欢

转载自blog.csdn.net/codejas/article/details/80466813