1、如何将类加载到jvm(双亲委派机制)
当AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。
当ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader```去完成。
如果BootStrapClassLoader加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),会使用ExtClassLoader来尝试加载;
若ExtClassLoader也加载失败,则会使用AppClassLoader来加载,如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException。
2、类加载详细过程
加载,查找并加载类的二进制数据,在Java堆中也创建一个java.lang.Class类的对象
连接,连接又包含三块内容:验证、准备、初始化。
- 1)验证,文件格式、元数据、字节码、符号引用验证;
- 2)准备,为类的静态变量分配内存,并将其初始化为默认值;
- 3)解析,把类中的符号引用转换为直接引用
初始化,为类的静态变量赋予正确的初始值
3、类加载完以后JVM干了什么?
在类加载检查通过后,接下来虚拟机将为新生对象分配内存。
4、JVM的内存模型
堆:存放对象实例
栈:描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧 用于存储局部变量表、操作栈、动态链接、方法出口等信息
本地方法栈:是为虚拟机使用到的Native方法服务 (Native访问操作系统底层)
方法区:存储已被虚拟机加载的类元数据信息 (运行时常量池、Classloader的引用,字段数据,方法数据等)
程序计数器:当前线程所执行的字节码的行号指示器
5、线程栈(虚拟机栈)
JVM规范让每个Java线程拥有自己的独立的JVM栈
当方法调用的时候,会生成一个栈帧。栈帧是保存在虚拟机栈中的,栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息
线程运行过程中,只有一个栈帧是处于活跃状态,称为“当前活跃栈帧”,当前活动栈帧始终是虚拟机栈的栈顶元素
6、JVM 年轻代到年老代的晋升过程的判断条件是什么?
部分对象会在From和To区域中复制来复制去,如此交换15次(由JVM参数MaxTenuringThreshold决定,这个参数默认是15),最终如果还是存活,就存入到老年代。
如果对象的大小大于Eden的二分之一会直接分配在old,如果old也分配不下,会做一次老年代GC,如果小于eden的一半但是没有足够的空间,就进行新生代GC。
新生代GC后,survivor仍然放不下,则放到老年代
动态年龄判断 ,大于等于某个年龄的对象超过了survivor空间一半 ,大于等于某个年龄的对象直接进入老年代
7、fullGC 很频繁,怎么去线上排查问题
是不是频繁创建了大对象(也有可能eden区设置过小)(大对象直接分配在老年代中,导致老年代空间不足--->从而频繁gc)
是不是老年代的空间设置过小了(Minor GC几个对象就大于老年代的剩余空间了)
8、类加载为什么要使用双亲委派模式,有没有什么场景是打破了这个模式?
双亲委派模式为了解决类载入过程中的安全性问题
自己编写了一个名为java.lang.Object的类,想借此欺骗JVM,然而双亲委托模型不会让他成功。因为JVM会优先在Bootstrap ClassLoader的路径下找到java.lang.Object 类,并载入它
打破双亲委派模式: 可以通过自定义ClassLoader,并重写父类的loadClass方法,来打破这一机制
1:自己写一个类加载器
2:重写loadclass方法(实现了双亲委派机制的逻辑)
3:重写findclass方法(获取要加载的类)
9、JVM垃圾回收机制,何时触发MinorGC等操作
eden区分配满的时候触发MinorGC(新生代的空间不够放的时候)
10、对象存活判断:
1、引用计数:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。
无法解决对象相互循环引用的问题。
2、可达性分析:从GC Roots开始向下搜索,搜索所走过的路径称为引用链 当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。不可达对象
11、垃圾收集算法:
1、标记-清除 :
算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象
缺点 :
标记和清除过程的效率都不高、
标记清除之后会产生大量不连续的内存碎片运行过程中需要分配较大对象时无法找到足够的连续内存 而不得不提前触发另一次垃圾收集动作
2、复制算法:
将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,
然后再把用完的内存空间一次清理掉
缺点 :
内存缩小为原来的一半,持续复制长生存期的对象则导致效率降低
3、标记-压缩(整理)算法:
将所有存活的对象压缩到内存的一端。之后,清理所有边界外的空间
12、回收器
Serial收集器:
串行收集器 新生代、老年代使用串行回收;新生代复制算法、老年代标记-压缩;垃圾收集的过程中会 服务暂停
ParNew收集器:
是Serial收集器的多线程版本,新生代并行,老年代串行;新生代复制算法、老年代标记-压缩
CMS收集器:
初始标记
并发标记
重新标记
并发清除
初始标记、重新标记这两个步骤 服务会暂停,重新标记阶段则是为了修正并发标记,
并发标记和并发清除过程中,收集器线程都可以与用户线程一起工作,并发阶段会降低吞吐量
13、java中存在的四种引用
1.强引用
Object object = new Object();
String str = "hello";
gc时不回收
只要某个对象有强引用与之关联,JVM必定不 会回收这个对象,即使在内存不足的情况下,JVM宁愿抛出OutOfMemory错误也不会回收这种对象
2.软引用
SoftReference<String> sr = new SoftReference<String>(new String("hello"));
软引用对象在gc时,在内存溢出前,会回收;
对于软引用关联着的对象,只有在内存不足的时候JVM才会回收该对象
这一点可以很好地用来解决OOM的问题,并且这个特性很适合用来实现缓存:比如网页缓存、图片缓存等
3.弱引用
WeakReference<String> sr = new WeakReference<String>(new String("hello"));
弱引用对象在gc时,不论内存使用情况都会回收;
弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象,
,如果存在强引用同时与之关联,则进行垃圾回收时也不会回收该对象
4.虚引用
ReferenceQueue<String> queue = new ReferenceQueue<String>();
PhantomReference<String> pr = new PhantomReference<String>(new String("hello"), queue);
虚引用对象在gc后,会发送一条通知给 ReferenceQueue 包装的对象;
一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收
虚引用必须和引用队列关联使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会把这个虚引用加入到与之 关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收