七月来临,各省高考分数已揭榜完成。而高考的完结并不意味着学习的结束,而是新旅程的开始。对于有志于踏入IT领域的高考少年们,这个假期是开启探索IT世界的绝佳时机。
为什么jvm引入了双亲委派机制
每个类加载器都有自己的命名空间 而命名空间中存储着类加载器加载过的类全限定名 在java中两个类是否相同时是通过 ClassLoaderId+PackageName+ClassName 进行判断 也就代表着是允许同时存在两个包名和类名完全一致的class的 为了打破这种类加载器之间的隔离性所以引出了双亲委派机制
双亲委派加载过程
1. 当App应用类加载器尝试加载一个类时 会先去自己的命名空间中查询是否加载过这个类 如果没有加载过则委托给父类加载Ext拓展类加载器进行加载
2. 当Ext拓展类加载器尝试加载一个类时 也会先去自己的命名空间中查询是否加载过这个类 如果没有加载过则委托给父类加载器Bootstrap根类加载器进行加载
3. 如果Bootstrap根类加载器加载失败 表示该类不在自己的加载范围之内 则交给Ext拓展类加载器进行加载
4. 如果Ext拓展类加载器加载失败 也表示该类不在自己的加载范围之内 则交给App应用类加载器进行加载
5. 如果App应用类加载器加载失败 表示根据类全限定名无法找到该类 则抛出ClassNotFoundException异常
JVM内存区域
JVM内存区域分为五个部分分别是
元数据空间:存储类信息(属性、方法、类元数据等一些信息)和运行时常量池 主要存储着编译器生成字面量等一些信息
堆:几乎所有new出来的对象都会在这里分配内存
程序计数器:线程所执行的行号指示器
虚拟机栈:也称为线程栈 与线程的数量一一对应 当线程调用一个方法后会生成一个栈针 栈针中记录着方法的变量表、返回值、返回地址等一些信息 一个方法的执行到结束就对应栈针出栈到入栈的过程
本地方法栈:线程所调用的native方法都会在这里进行登记
Java7-8堆空间(逻辑+物理分代)分为新生代和老年代 新生代中有一个Eden区和两个Survivor区(分别是form区和to区) 老年代有一个Old区 新生代与老年代的空间比例为1:2 新生代中Eden区和Survivor区的空间比例为8:1:1
Java9堆空间(逻辑分代)会划分为了一个个大小相等的region区 新生代默认初始占用堆空间的比例为5% 最多不能超过60% 同时额外新增了一个区Humongous来存放大对象(超过region区空间的一半就认为是大对象)
Java11堆空间(逻辑+物理都不分代)将region区划分为了大中小三个等级 小存放对象大小<256KB的对象 中存放对象>=256KB并且<=4MB的对象 大存放>4MB以上的对象
内存溢出和内存泄露的区别
内存溢出(原因:堆内存空间不够用时会进行垃圾回收 回收的过程中发现堆中的对象依旧存活 此时又没有新的内存空间分配新的对象实例 从而发生内存溢出)是指 比如申请了10MB的内存大小 但是运行过程中产生了11MB的数据 导致空间不够用 从而发生内存溢出
内存泄露是指 申请了一块内存 使用之后后续不会再使用了 也没有进行释放 GC也无法对其进行回收 从而造成内存的浪费
判断Java对象是否存活的方法
1. 引用计数器算法
对象本身带一个引用计数器 当有对象引用指向它时就会+1 随着方法结束 栈帧中局部变量表中记录的对象引用也会随之进行销毁 此引用器就会-1 当它为0时被认定是一个没有任何对象引用的垃圾对象 垃圾回收的时候就会进行回收
缺点:对象循环引用时无法正确的判定
2. 可达性分析算法
在可达性分析算法中存在一个GCRoots的概念 在发生GC时 会以这些可以作为GCRoots的根结点(局部变量表中引用的对象、静态对象等) 从上往下进行搜索 根不可达的对象被认定为垃圾对象
垃圾回收算法
1. 标记清除算法
标记清除算法分为两个阶段 分别是标记阶段和清除阶段 在标记阶段根据可达性分析算法分析出的存活对象进行标记 然后在清除阶段 清除那些未被标记的对象(GC标志位默认位0 标记以后会变成1 清除完成后会初始化为0)
缺点:产生大量的内存碎片 在标记阶段会STW
2. 复制算法
将原有的堆内存分成两块区域 只使用一块区域用于对象的分配 当被填满后就会触发GC机制 此时会将存活的对象移动至另一块区域中 然后对之前的那块区域进行全面清除
缺点:在一定程度上造成了内存的浪费
被应用于新生代 因为新生代中存放的对象都是朝夕生死的 对象的存活率不是很高 所以复制起来效率也能有所保证
3. 标记整理算法
标记整理算法也分为两个阶段 分别是标记和整理阶段 在标记阶段也是根据可达性分析算法分析出的存活对象对其进行标记 标记完成后将存活的对象进行压缩整理到空闲内存中 然后对存活对象边界之外的对象进行统一回收被应用于老年代中