1、什么是类的加载?
1、类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个 java.lang.Class对象,用来封装类在方法区内的数据结构。
2、加载.class文件的方式:
从本地系统中直接加载
通过网络下载.class文件
从zip,jar等归档文件中加载.class文件
从专有数据库中提取.class文件
将Java源文件动态编译为.class文件
2、类的生命周期
加载、验证、准备、解析、初始、使用和卸载。
加载、验证、准备和初始化这四个阶段发生的顺序是确定的,而解析阶段则不一定,它在某些情况下可以在初始化阶段之后开始,这是为了支持Java语言的运行时绑定(也成为动态绑定或晚期绑定)。
1. 类的加载过程
通过一个类的全限定名来获取其定义的二进制字节流。
将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
在Java堆中生成一个代表这个类的 java.lang.Class对象,作为对方法区中这些数据的访问入口。
2. jvm的初始化
1、初始化,为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始化。在Java中对类变量进行初始值设定有两种方式:
①声明类变量是指定初始值
②使用静态代码块为类变量指定初始值
2、JVM初始化步骤
1、假如这个类还没有被加载和连接,则程序先加载并连接该类
2、假如该类的直接父类还没有被初始化,则先初始化其直接父类
3、假如类中有初始化语句,则系统依次执行这些初始化语句
3、类初始化时机:只有当对类的主动使用的时候才会导致类的初始化,类的主动使用包括以下六种:
创建类的实例,也就是new的方式
访问某个类或接口的静态变量,或者对该静态变量赋值
调用类的静态方法
反射(如 Class.forName(“com.shengsiyuan.Test”))
初始化某个类的子类,则其父类也会被初始化
Java虚拟机启动时被标明为启动类的类( JavaTest),直接使用 java.exe命令来运行某个主类
3、双亲委派模型
双亲委派模型的工作流程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。
双亲委派机制:
1、当 AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。
2、当 ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader```去完成。
3、如果 BootStrapClassLoader加载失败(例如在 $JAVA_HOME/jre/lib里未查找到该class),会使用 ExtClassLoader来尝试加载;
4、若ExtClassLoader也加载失败,则会使用 AppClassLoader来加载,如果 AppClassLoader也加载失败,则会报出异常 ClassNotFoundException。
4、JVM内存空间
1、栈:存放局部变量
2、堆:存放所有new出来的东西
由年轻代和老年代组成
老年代空间大小=堆空间大小-年轻代大空间大小
3、方法区:被虚拟机加载的类信息、常量、静态变量等。
方法区有时被称为持久代(PermGen)。
别名叫做Non-Heap(非堆)
4、程序计数器(和系统相关)
每条线程都需要有一个独立的程序计数器,各条线程之间的计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。
5、本地方法栈
与虚拟机栈所发挥的作用是非常相似
为虚拟机使用到的Native方法服务
5、GC算法
新生代的GC称为minor GC ,
把老年代的GC成为 full GC,
所谓full gc会先出发一次minor gc,然后在进行老年代的GC。
1、对象存活判断
引用计数:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。此方法简单,无法解决对象相互循环引用的问题。可达性分析(Reachability Analysis):从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。不可达对象。
2、垃圾收集算法
1、标记-清除算法
算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。
缺点有两个:一个是效率问题,标记和清除过程的效率都不高;另外一个是空间问题,标记清除之后会产生大量不连续的内存碎片
2、复制算法
将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
缺点:这种算法的代价是将内存缩小为原来的一半,持续复制长生存期的对象则导致效率降低。
3、标记-整理算法
该算法与标记-清除算法类似,都是先对存活的对象进行标记,但是在清除后会把活的对象向左端空闲空间移动,然后直接清理掉端边界以外的内存。
4、分代收集算法
把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。
新生代中,存活率低,使用复制算法;
存活率较高,使用“标记-清除”算法或者“标记-整理”算法。
6、垃圾收集器
1、Serial收集器
新生代、老年代使用串行回收;
新生代复制算法、老年代标记-整理;垃圾收集的过程中会Stop The World(服务暂停)
2、ParNew收集器
Serial收集器的多线程版本;
新生代并行,老年代串行;
新生代复制算法、老年代标记-整理
3、Parallel收集器
类似ParNew收集器,Parallel收集器更关注系统的吞吐量
新生代复制算法、老年代标记-整理
4、CMS收集器
是一种以获取最短回收停顿时间为目标的收集器。
基于“标记-清除”算法实现的,运作过程更复杂,分为4个步骤:
• 初始标记(CMS initial mark)
• 并发标记(CMS concurrent mark)
• 重新标记(CMS remark)
• 并发清除(CMS concurrent sweep)
其中初始标记、重新标记这两个步骤仍然需要“Stop The World”。初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快,并发标记阶段就是进行GC Roots Tracing的过程,而重新标记阶段则是为了修正并发标记期间,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。
整个过程中耗时最长的并发标记和并发清除过程中,收集器线程都可以与用户线程一起工作,所以总体上来说,CMS收集器的内存回收过程是与用户线程一起并发地执行。老年代收集器(新生代使用ParNew)
优点: 并发收集、低停顿缺点: 产生大量空间碎片、并发阶段会降低吞吐量
5、G1收集器
与CMS收集器相比G1收集器有以下特点:
1、空间整合
G1收集器采用标记整理算法,不会产生内存空间碎片。分配大对象时不会因为无法找到连续空间而提前触发下一次GC。
2、可预测停顿
这是G1的另一大优势,降低停顿时间是G1和CMS的共同关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度 为N毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒,这几乎已经是实时Java(RTSJ)的垃圾收集器的特征了。
使用G1收集器时,Java堆的内存布局与其他收集器有很大差别,它将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔阂了,它们都是一部分(可以不连续)Region的集合。
3、收集步骤:
1、标记阶段,首先初始标记(Initial-Mark),这个阶段是停顿的(Stop the World Event),并且会触发一次普通Mintor GC。
2、Root Region Scanning,程序运行过程中会回收survivor区(存活到老年代),这一过程必须在young GC之前完成。
3、Concurrent Marking,在整个堆中进行并发标记(和应用程序并发执行),此过程可能被young GC中断。在并发标记阶段,若发现区域对象中的所有对象都是垃圾,那个这个区域会被立即回收。同时,并发标记过程中,会计算每个区域的对象活 性(区域中存活对象的比例)。
4、Remark, 再标记,会有短暂停顿(STW)。再标记阶段是用来收集 并发标记阶段 产生新的垃圾(并发阶段和应用程序一同运行);G1中采用了比CMS更快的初始快照算法:snapshot-at-the-beginning (SATB)。
5、Copy/Clean up,多线程清除失活对象,会有STW。G1将回收区域的存活对象拷贝到新区域,清除Remember Sets,并发清空回收区域并把它返回到空闲区域链表中。
6、 复制/清除过程后。回收区域的活性对象已经被集中回收
7、jvm调优-工具
jvm监控分析工具一般分为两类,一种是jdk自带的工具,一种是第三方的分析工具。
jdk自带工具一般在jdk bin目录下面:jconsole.exe和jvisualvm.exe;
第三方的分析工具有很多:较有代表性的:MAT(Memory Analyzer Tool)、GChisto等。
8、jvm 总体梳理
1、jvm体系总体分四大块:
• 类的加载机制
• jvm内存结构
• GC算法 垃圾回收
• GC分析 命令调优
2、类的加载机制
主要关注点:
• 什么是类的加载
• 类的生命周期
• 类加载器
• 双亲委派模型
类的生命周期
• 加载,查找并加载类的二进制数据,在Java堆中也创建一个java.lang.Class类的对象
• 连接,连接又包含三块内容:验证、准备、初始化。1)验证,文件格式、元数据、字节码、符号引用验证;2)准备,为类的静态变量分配内存,并将其初始化为默认值;3)解析,把类中的符号引用转换为直接引用
• 初始化,为类的静态变量赋予正确的初始值
• 使用,new出对象程序中使用
• 卸载,执行垃圾回收
3、jvm内存结构
主要关注点:
• jvm内存结构都是什么
• 对象分配规则
4、GC算法 垃圾回收
主要关注点:
• 对象存活判断
• GC算法
• 垃圾回收器
5、GC分析 命令调优
主要关注点:
• GC日志分析
• 调优命令
• 调优工具
JDK监控和故障处理命令有jps、jstat、jmap、jhat、jstack、jinfo
原文链接:https://zhuanlan.zhihu.com/p/34426768