类的加载过程?
大致分为三步,加载,链接,初始化
1.加载:将类的class文件读入到内存中,生成java.long.class文件
2.链接分为3步:验证,准备,解析;
验证阶段:顾名思义就是验证被加载的类内部结构是否正确
准备:准备给被加载的类变量分配内存,并且设置默认初始值;
解析:将类中的符号引用转换成实际引用
3初始化:将类变量进行初始化
流程图
类加载器有哪些?
1. 启动类加载器
2. 扩展类加载器
3.应用程序类加载器
4.自定义加载器
什么是双亲委派
当一个类加载器的到类加载的请求时,他不会第一时间选择自己加载,而是委派到他的父类加载器去加载,如果父类还是加载不了,再继续向上委托,如果最顶端的启动类加载器还解决不了,会再一次向下由自己的子类加载去加载,最后都执行不了的话,就报classNotFindException
为什么要有双亲委派?这么设计是为什么?
1.安全性
2.防止类重复加载
如果我自己写了一个java.long.String,然后各个类自行加载的话,会出现有多个类加载到内存中,类之间的唯一性就没法保证了
如何破坏双亲委派
继承classlodel,复写loadlclass方法
jvm的内存模型分类
1.堆:
对大多数new的对象都放在这里
2.方法栈
2.1本地方法栈
用来存储native方法
2.2虚拟机栈
存储自己写的方法的栈信息
3.程序计数器
记录程序执行到哪了
4.方法区
存储类的信息,常量,静态变量
JVM垃圾回收
什么是垃圾?
垃圾就是当一些对象不会被使用,但是还会占用内存空间,那这些对象就是垃圾
怎么定义某些对象就是垃圾呢?
既然不会被使用的对象就是垃圾,但是如何界定这个对象不会被使用了呢?这里引出两个方案
1.引用计数法
这个方法就是通过对每个对象维护一个计数器,能记录一下有多少个引用指向该对象,当计数器为0的时候会被认为是垃圾
有什么缺点?
当循环引用的时候 比如 A->B B->A 因为相互引用,计数器无法归零,那无论什么以后都不会认定成为垃圾
2.可达性分析算法
如果这个对象被gcroot(在某一时刻一定不会被回收的对象)直接或者间接的持有,那么这个对象就不会被回收(为了解决引用计数法的ab相互引用的问题)
什么对象可以视作为GCroot?
栈中的静态变量
方法区中的局部变量
方法区中的常量
缺点?
在解决循环引用的时候,如果两个对象都没有gcroot标记,那这两个对象被视作垃圾,但实际上业务场景两个对象还是要用的,结果是被回收掉了(和引用计数法相反)
用什么方式清理垃圾(垃圾回收算法)
标记清楚
根据GCroot,对存活的对象打上标记,然后把剩余需要的对象清除掉
缺点:
会存留出大量的空间碎片,空间利用例比较低
标记复制
根据GCroot,把一定不能被清除的对象,会放到一块区域后,然后剩下直接清除掉
缺点:
会需要单独了一片区域用来存储存活对象
标记整理
是对标记清除的一种优化,根据gcroot,把存活对象标记起来,然后整理到一起后,剩下的直接清除掉
缺点:
因为标记完需要整理所以效率会低
垃圾被回收的步骤
青年代中分为伊甸区和两个幸存者区,当需要进行gc的时候,先是利用标记复制算法,将不需要gc的对象放入幸存者区内,然后再将剩下的对象直接全部清除,每清除一次幸存者区的对象年龄+1,当加到15的时候会放入老年代,然后等待老年代进行gc
但是这里有一个情况,当如果直接new了一个大对象的话会直接放入老年代中。
青年代和老年代都用的哪些回收算法
青年代:标记复制
首先要明白青年代里面对象的特点,青年代空间有限,大部分对象是需要被回收的,而且清除频次高。把需要用的对象先放在幸存者区内,然后剩下的垃圾对象直接一股脑全部清除
老年代:标记清除+标记整理(CMS)
老年代的特点,空间大,东西多,大部分对象都不需要被回收(因为在青年代已经被清理15次还没有被回收,能进入老年代都是非等闲之辈),清除的频次也不高。
CMS清除的步骤:
清除的时候先用标记清除先清一变,但是不会整理(因为你清一变可能只清了几个对象而已,整理的话太浪费性能了),但是可能经过20次的清理后,那就得需要整理了,因为出现了大量的空间碎片