JAVA类加载器
双亲委派模型
启动类加载器->扩展类加载器->应用类加载器->自定义类加载器
启动类加载器(rt.jar如io包,lang包)
扩展类加载器(%JAVA_HOME%/lib/ext/)
应用类加载器(classpath)
自定义类加载器(自定义路径,如自己导的包自己写的类)
类加载时如果未加载,会往上找类加载器,从而保证类的安全性不会被覆盖掉,如不可以重写一个String类,因为在启动类加载器时就加载完毕,你如果再要加载你自己写的Stirng类,就会往上找到启动类加载器,然后发现Stirng已经被加载过了从而加载失败。
类加载过程
加载
取得类的二进制字节流,通过类的全限定名称(包名+类名)
把二进制字节流中静态存储结构转化为方法区数据结构
在内存中生成代表这个类的java.lang.Class对象,这里是放在堆中
连接
验证(验证类的正确性,是否遵循jvm规范)
文件格式的验证
元数据验证
字节码验证
符号引用验证
准备(为类里面的变量分配内存,将一些变量初始化)
解析(将类的一些符号引用转换为直接引用)
初始化(将静态方法块等组装为初始化方法然后去初始化)
执行静态代码块,静态变量,如果这个类还没有被加载和链接,就先加载和链接,有直接父类的话就先初始化父类,如果有初始化语句的话就按顺序(根据你写的代码顺序)执行初始化语句
类的构造器<clinit>:由编译器自动完成收集
static{}语句
static变量
JVM模型
VM Stack栈
线程私有
方法在执行时会创建一个栈帧,用于存储局部变量表,操作数栈,动态链接(对常量的引用),方法出口等信息
方法从调用到执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程
局部变量表所需要的内存空间在编译期间完成分配,而且分配多大的局部变量空间是完全确定的,在方法运行期间不会改变其大小
出栈后空间释放
heap区 堆
线程共享
存储对象或者数组
heap区划分:
年轻代(Eden,s0,s1),老年代(Tenured),1.7以前还有一个永久代,new出来的对象先存在eden区,如果new出来的对象eden没空间放了就会GC,如果分配到s区的对象超过s区空间大小的一半就会直接放到老年代
heap内存分配参数:
-Xms:最小分配内存
-Xmx:最大分配内存
-Xmn:年轻代大小
-XX:NewRatio 新生代和老年代的比值
-XX:SurvivorRatio Eden区和两个幸存者区的比值 2s:1E
Method Area方法区
线程共享
存储
类信息
常量
静态常量
方法字节码
Program Counter Register程序计数器
作用
当前线程执行的字节码的信号指示器,通过改变此指数器来选取下个需要执行的字节码指令
特征
在线程创建时创建
每个线程拥有一个
指向下一条指令的地址
JVM运行时数据区
JMM模型
线程工作内存,主内存
JVM内存回收
标记-清除算法
标记阶段
标记存活对象
清除阶段
统一回收所有未标记的对象 缺点 会产生内存碎片 如果空间内存碎片太多,当程序产生大对象无法在堆中找到连续空间 大小存放的时候,会强制发生GC
复制算法
原理
内存一分为二,每次只使用其中一块,当一块内存没有连续空间存储对象的时候,会把存活下来的对象复制到另外一 块内存中,然后一次性清除之前的哪块空间
没有内存碎片问题
代价就是讲内存减少了一半,空间利用率不高 不适用于存活对象较多的场景,比如老年代 而实际上我们并不需要按照 1:1 的比例来划分,因为大部分对象从创建到结束这个生命周期很短 HotSpot虚拟机默认Eden:Survivor=8:1
标记-整理算法
原理 标记存活对象,然后把存活对象向一端移动 清理掉存活对象这端以外的所有空间
优缺点
适合用于存活对象较多的场合,如老年代 解决了空间碎片和效率问题: 将所有的存活对象压缩到内存的一端,然后清理边界外所有的空间
分代收集算法
分代思想
堆划分为新生代和老年代 新生代中,能够存活的对象很少,可以使用复制算法 老年代中,对象存活率高,而且没有 额外的空间用来做老年代的担保,可以使用标记清除或者标记整理算法
垃圾收集器-GC
Serial:
新生代串行收集器(GC的时候用户线程暂停) 新(复制算法),老(标记整理)
ParNew:
新生代并行收集器
Parallel Scavenge(默认) 新生代并行收集器
目标:尽可能缩GC时用户线程的停顿时间 在注重吞吐量或CPU资源敏感的场合,可以优先考虑Parallel Scavenge收集器 + Parallel Old收集器
Serial Old 老年代串行收集器
Parallel Old 老年代并行收集器
CMS 真正意义上的并发收集器(老年代收集器) 目标:最短的GC停顿时间
G1
分代收集
并发:用户线程和GC线程可以同时执行,如果发生GC,用户线程依然可以执行
并行:用户线程和GC线程可以同时执行,如果发生GC 用户线程会暂停