13.JAVA类加载器有哪些?
JDK自带三个类加载器:bootstrap ClassLoader,ExtClassLoader,AppClassLoader。
(1)BootstrapClassLoader(顶层加载器)是ExtClassLoader的父类加载器,默认负责加载%JAVA——HOME%lib下的jar包和class文件。
(2)ExtClassLoader(扩展类加载器)是AppClassLoader的父类加载器,负责加载%JAVA_HOME%lib/ext文件夹下的jar包和class类。
(3)AppClassLoader(应用程序类加载器)是自定义加载器的父类,负责加载classpath下类文件。【系统类加载器,线程上下文加载器】
继承ClassLoader实现自定义加载器。
14.双亲委派模型
首先类加载器主要是复杂从各种渠道将我们的字节码文件加载我们的内存中,然后底层会调用native方法将我们的类相关信息写入到我们元空间中,主要保存我们类的所有信息,然后会在方法区创建当前类的class对象,方便我们后续通过java代码获取类信息,里面会持有一个指向元空间中类的指针。
类加载器的话主要有四大中,系统、扩展、应用以及自定义类加载器,内部类加载的机制采用的是双亲委派机制,主要目的是防止我们的核心类库被修改,以及我们的类被重复加载。(过程)底层原理其实就是加载类的时候层层向上委托,线上顶层类加载器判断有没有加载过这个类,在尝试加载,如果加载不到再层层向下查找类加载路径,重复上述步骤,直到找到类加载路径并返回。
例如我们自定义一个包名和类名完全相同的String,他会层层向上传递系统类加载器发现没有加载过尝试加载,结果一下子加载到了,那就用它加载的,别人都别忙活了,底层的实现其实就是在我们defineclass方法中实现的。当然我们也可以通过实现Classloader来实现我们自定义的类加载逻辑,甚至打破双亲委派机制,比如:我们可以使用类加载器对我们的字节码进行加解密等。
所以,在java中所有以java.开头的核心类都不能被替换。
好处:
1.避免了类的重复加载,当父类的类加载器已经加载好后,自己就不用加载了,保证
类的唯一性。
2.避免java的核心API被篡改。
15.JAVA中异常体系
Java中所有异常体系都来自顶级父类Throwable。
Throwable下有两个子类Error (例如内存溢出错误OOM)和Exception(例如空指针,下标越界等)。
Error是程序无法处理错误,一旦出现这个错误,则程序被迫停止运行。
Exception不会导致程序停止,其又分为RunTimeException(运行时异常)与CheckedException(检查异常)。
RunTimeException常常发生在程序运行过程中,会导致程序当前线程执行失败。CheckedException常常发生在程序编译过程中,会导致程序编译不通过。
16.GC如何判断对象可以被回收
主要有两种方法:
- 引用计数法:每个对象都有一个计数属性,新增一个引用时计数器就加1,计数器为0时,表示当前对象无被任何对象引用则可以回收(是否可以被回收判断条件)。
优点:实现简单,也方便辨别是否可以被回收。
缺点:需要单独的字段存储计数器,会导致存储空间开销变大;而每次都得重新更新计数器中值,增加了时间开销。最严重的问题是,无法处理循环引用的情况(即A引用B,B又引用A,导致他们不在使用时候计数器仍然值为1)。
- 可达性分析法:
- 从GC Roots为起点,开始向下搜索被根对象集合所连接的目标对象是否可达。
- 使用可达性分析算法后,内存中存活的对象都会被根对象集合直接或者间接连接,搜索所走过的路径称为引用链。
- 如果目标对象没有任何引用链相连,则标记为不可达,就意味着对象已经死亡,可以标记为垃圾对象。(只有被根对象集合直接或间接连接的对象才是存活对象)。
优点:解决引用指针法的循环引用问题。
GC Roots的对象有:
1.虚拟机栈中的引用对象;
2.方法区中类静态属性引用的对象;
3.方法区中常量引用的对象;
4.本地方法栈中JNI(即Native方法【本地方法】,会自动调用)引用的对象。
Root判断技巧:由于Root采用栈方式存放变量和指针,所以如果一个指针,它保存了堆内存里面的对象,但是自己又不存放在堆内存里面,那它就是一个Root。
可达性算法中不可达对象并非立刻死亡,对象还有一次自我救赎机会,对象至少被两次标记:第一次是经过可达性分析发现没有与GC Roots连接的引用链,第二次是在由虚拟机自动建立的Finalize队列中判断是否需要执行finalize()方法。
17.JVM中那些是共享区?哪些可以作为gc root?
方法区和堆区基本是共享数据的,程序计数器,本地方法栈、栈是每个线程独有的。
2.JVM在进行垃圾回收时候,需要找到“垃圾”对象,但是直接找“垃圾“对象有些麻烦,所以反过来,先找非”垃圾“对象,也就是正常对象,那么就需要从某些根的引用路径找到正常对象,而这些根有一个特征,他们只会引用其他对象,而不会被其他对象引用,就可以作为GC root。