面试前先看看这24道JVM面试题,成功更有把握
1.什么是JVM?
JVM 的全称是 「Java Virtual Machine」,也就是我们耳熟能详的 Java 虚拟机。它能识别 .class后缀的文件,并且能够解析它的指令,最终调用操作系统上的函数,完成我们想要的操作。
C++开发出来的程序,编译成二进制文件后,就可以直接执行了,操作系统是能够识别的。
但是咱们的Java程序就不一样了,使用javac命令编译出来的的.class文件之后,操作系统是不能识别的,需要对应JVM去做一个转换后,操作系统才能识别。
2.说说JDK、JRE、JVM的关系?
JDK是Sun公司(已被Oracle收购)针对Java开发员的软件开发工具包。自从Java推出以来,JDK已经成为使用最广泛的Java SDK(Software development kit)。
JRE全称Java Runtime Environment,是运行基于Java语言编写的程序所不可缺少的运行环境。也是通过它,Java的开发者才得以将自己开发的程序发布到用户手中,让用户使用。
JVM 就是 Java 虚拟机,它能识别 .class后缀的文件,并且能够解析它的指令,最终调用操作系统上的函数,完成我们想要的操作。
JDK中包含JRE,也包括JDK,而JRE也包括JDK。
范围关系:JDK>JRE>JVM。
3.获取class文件有哪些方式?
从本地文件系统中加载.class文件
从jar包中或者war包中加载.class文件
通过网络或者从数据库中加载.class文件
把一个Java源文件动态编译,并加载,加载进来后就,系统为这个.class文件生成一个对应的Class对象。
4.生成Class对象的有哪些方式?
1.对象获取。调用person类的父类方法getClaass();
2.类名获取。每个类型(包括基本类型和引用)都有一个静态属性,class;
3.Class类的静态方法获取。forName("字符串的类名")写全名,要带包名。(包名.类名)
5.类加载器有哪些?
Bootstrap ClassLoader
负责加载$JAVA_HOME中 jre/lib/rt.jar里所有的class或Xbootclassoath选项指定的jar包。由C++实现,不是ClassLoader子类。
Extension ClassLoader
负责加载Java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或 -Djava.ext.dirs指定目录下的jar包。
App ClassLoader
负责加载classpath中指定的jar包及 Djava.class.path所指定目录下的类和jar包。
Custom ClassLoader
通过java.lang.ClassLoader的子类自定义加载class,属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader。
6.如何自定义类加载器?
用户根据需求自己定义的。需要继承自ClassLoader,重写方法findClass()。
如果想要编写自己的类加载器,只需要两步:
继承ClassLoader类
覆盖findClass(String className)方法
ClassLoader超类的loadClass方法用于将类的加载操作委托给其父类加载器去进行,只有当该类尚未加载并且父类加载器也无法加载该类时,才调用findClass方法。如果要实现该方法,必须做到以下几点:
1.为来自本地文件系统或者其他来源的类加载其字节码。2.调用ClassLoader超类的defineClass方法,向虚拟机提供字节码。
7.如何设置Java虚拟机栈的大小呢?
可以使用虚拟机参数-Xss 选项来设置线程的最大栈空间,栈的大小直接决定了函数调用的最大可达深度;
-Xss size
8.方法区、堆、栈之间到底有什么关系?
栈指向堆
如果在栈帧中有一个变量,类型为引用类型,比如
package com.tian.my_code.test;
public class JvmCodeDemo {
public Object testGC(){
int op1 = 10;
int op2 = 3;
Object obj = new Object();
Object result=obj;
return result;
}
}
这时候就是典型的栈中元素obj指向堆中的Object对象,result的指向和obj的指向为同一个对象。
使用命令
javac -g:vars JvmCodeDemo.java
进行编译,然后再使用
javap -v JvmCodeDemo.class >log.txt
然后打开log.txt文件
方法区指向堆
方法区中会存放静态变量,常量等数据。
如果是下面这种情况,就是典型的方法区中元素指向堆中的对象。【「红线」】
堆指向方法区
方法区中会包含类的信息,对象保存再堆中,创建一个对象的前提是有对应的类信息,这个类信息就在方法区中。
9.Java对象内存是如何布局的?
一个Java对象在内存中包括3个部分:对象头、实例数据和对齐填充。
10.如何理解Minor/Major/Full GC?
还可能问你:请说一下Minor/Major/Full GC分别发送在哪个区域。
❝
Minor GC:发生在年轻代的 GC Major GC:发生在老年代的 GC。Full GC:新生代+老年代,比如 Metaspace 区引起年轻代和老年代的回收。
❞
11.能够触发条件 Full GC 有哪些?
1)调用System.gc时,系统建议执行Full GC,但是不必然执行;
2)老年代空间不足;
3)方法去空间不足;
4)通过Minor GC后进入老年代的平均大小 > 老年代的可用内存;
5)由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小。即老年代无法存放下新年代过度到老年代的对象的时候,会触发Full GC。
12.为什么需要Survivor区?
如果没有Survivor,Eden区每进行一次Minor GC ,并且没有年龄限制的话, 存活的对象就会被送到老年代。这样一来,老年代很快被填满,触发Major GC(因为Major GC一般伴随着Minor GC,也可以看做触发了Full GC)。老年代的内存空间远大于新生代,进行一次Full GC消耗的时间比Minor GC长得多。
执行时间长有什么坏处?频发的Full GC消耗的时间很长,会影响大型程序的执行和响应速度。
可能你会说,那就对老年代的空间进行增加或者较少咯。
假如增加老年代空间,更多存活对象才能填满老年代。虽然降低Full GC频率,但是随着老年代空间加大,一旦发生Full GC,执行所需要的时间更长。
假如减少老年代空间,虽然Full GC所需时间减少,但是老年代很快被存活对象填满,Full GC频率增加。
所以Survivor的存在意义,就是减少被送到老年代的对象,进而减少Full GC的发生,Survivor的预筛选保证,只有经历16 次Minor GC还能在新生代中存活的对象,才会被送到老年代。
13.为什么需要两个大小一样的Survivor区?
最大的好处就是解决了碎片化。
假设现在只有一个Survivor区,我们来模拟一下流程:
刚刚新建的对象在Eden中,一旦Eden满了,触发一次Minor GC,Eden中的存活对象就会被移动到Survivor区。这样继续循 环下去,下一次Eden满了的时候,问题来了,此时进行Minor GC,Eden和Survivor各有一些存活对象,如果此时把Eden区的 存活对象硬放到Survivor区,很明显这两部分对象所占有的内存是不连续的,也就导致了内存碎片化。
永远有一个Survivor space是空的,另一个非空的Survivor space无碎片。
14.新生代中Eden:S1:S2为什么是8:1:1?
新生代中的可用内存:复制算法用来担保的内存为9:1,所以只会造成 10% 的空间浪费。可用内存中Eden:S1区为8:1 即新生代中Eden:S1:S2 = 8:1:1
这个比例,是由参数 -XX:SurvivorRatio 进行配置的(默认为 8)。
15.如何判断对象已死?
引用计数法
给对象添加一个引用计数器,每当一个地方引用它object时技术加1,引用失去以后就减1,计数为0说明不再引用
优点:实现简单,判定效率高
缺点:无法解决对象相互循环引用的问题,对象A中引用了对象B,对象B中引用对象A。
public class A {
public B b;
}
public class B {
public C c;
}
public class C {
public A a;
}
public class Test{
private void test(){
A a = new A();
B b = new B();
C c = new C();
a.b=b;
b.c=c;
c.a=a;
}
}
可达性分析算法
当一个对象到GC Roots没有引用链相连,即就是GC Roots到这个对象不可达时,证明对象不可用。
GC Roots种类:
❝
Java 线程中,当前所有正在被调用的方法的引用类型参数、局部变量、临时值等。也就是与我们栈帧相关的各种引用。所有当前被加载的 Java 类。Java 类的引用类型静态变量。运行时常量池里的引用类型常量(String 或 Class 类型)。JVM 内部数据结构的一些引用,比如 sun.jvm.hotspot.memory.Universe 类。用于同步的监控对象,比如调用了对象的 wait() 方法。
❞
public class Test{
private void test(C c){
A a = new A();
B b = new B();
a.b=b;
//这里的a/b/c都是GC Root;
}
}
16.对象的有几种引用类型?
强引用:User user=new User();我们开发中使用最多的对象引用方式。
特点:我们平常典型编码Object obj = new Object()中的obj就是强引用。
通过关键字new创建的对象所关联的引用就是强引用。
当JVM内存空间不足,JVM宁愿抛出OutOfMemoryError运行时错误(OOM),使程序异常终止,也不会靠随意回收具有强引用的“存活”对象来解决内存不足的问题。
对于一个普通的对象,如果没有其他的引用关系,只要超过了引用的作用域或者显式地将相应(强)引用赋值为 null,就是可以被垃圾收集的了,具体回收时机还是要看垃圾收集策略。
软引用:SoftReference