一.JVM概述
1.JVM的作用
把字节码编译为机器码去执行,负责把字节码装载到虚拟机中

2.JVM的组成部分
- 类加载器(ClassLoader)
- 运行时数据区(Runtime Data Area)
- 本地方法库(Execution Engine)
- 执行引擎(Native Interface)
程序在执行过程中需要把java文件转换为字节码文件,然后通过类加载器将文件存储在内存中的运行时数据区,而字节码文件是JVM的一套指令集规范,并不能直接交给底层系统去执行,因此需要特定命令的解析器执行引擎,将字节码编译为底层系统指令载交给cpu去执行,而这个过程中需要调用本地方法接口去实现整个程序的功能.
二.JVM类加载
1.类加载概述
类加载子系统负责从文件系统或网络中加载字节码文件,它只负责加载字节码文件,至于可不可以运行,则有执行引擎决定
2.类加载过程
2.1加载
使用流将硬盘上的字节码文件加载到内存中的运行时数据区的方法区,并生成对象的class对象
2.2链接
链接又分为三个步骤:
-验证:验证字节码格式是否正确,对字节码描述的信息进行语义分析,以保证其描述的信息符合java的语言规范
-准备:为类中的静态属性进行赋值(赋的值为0,在初始化阶段在进行赋值)
static int a=123; 准备阶段a的值为0,在初始化阶段才赋值为123
-解析:将字节码中的逻辑引用替换为直接引用
逻辑引用:字节码中的逻辑符号
符号引用:内存地址
2.3初始化
为类的静态变量进行正确的赋值
类什么时候会初始化?
类只要被用到了都会被加载
1.new对象
2.加载子类时
3.反射机制
4.运行类中的main方法
5.使用了类的静态成员(类.静态变量 类.静态方法)
类什么时候不会被加载?
1.只访问了类的静态常量
2.创建的数组对象,只是作为类型存在
3.类加载器的分类
在jvm角度来说,应该被分为俩类:
1.引导类加载器,使用 C/C++语言实现,嵌套在 JVM 内部.它用来加载 java 核心类库.
2.其他所有类加载器,这些类加载器全部由java语言实现,独立存在于jvm外部,并且全部继承自抽象类 java.lang.ClassLoader
站在java程序员的角度应该被分为三类:
1.引导类加载器
2.扩展类加载器,从 java.ext.dirs 系统属性所指定的目录中加载类库,或从 JDK 系统安装目录jre/lib/ext 子目录(扩展目录)下加载类库
3.应用程序类加载器,加载我们自己定义的类,用于加载用户类路径(classpath)上所有的类
4.双亲委派机制
java虚拟机对class文件采用的是按需加载的方式,也就是需要用到类时才把他的class文件加载到内存中生成class对象,在加载某个类的字节码文件时,jvm采用了双亲委派机制,即把请求交给父类处理,是一种任务委派机制.
工作原理:
如果一个类加载器收到了类加载请求,这个类加载器并不会自己先加载,而是将请求委托给父类的加载器去执行,如果父类加载器还存在父类,则继续向上委托,一次递归,直至得到顶层的启动类加载器,如果父类的加载器可以完成加载任务就成功返回,然后无法完成,子类才会自己尝试加载,如果均加载失败,就会抛出 ClassNotFoundException 异常
优点:安全,可以避免自己写的类替换java的核心类
如何打破双亲委派机制?
可以自定义类加载器
在 ClassLoader 类中涉及类加载的方法有两个,loadClass(String name), findClass(String name),这两个方法并没有被 final 修饰,也就表示其他子类可以重写.
三.运行时数据区
1.运行时数据区组成概述
2.程序计数器
用来记录下一条指令的地址,也就是即将要执行的指令,由执行引擎执行
特点:
它是一块很小的内存区域,也是运行速度最快的存储区域
在JVM规范中,每个线程都有一个计数器,是线程私有的,他的生命周期与线程一致会记录当前线程正在执行的java程序的JVM指令地址是唯一一个JVM规范中没有规定任何OutOfMemoryError情况的区域
3.虚拟机栈

4.堆
4.1堆内存的划分
在JDK1.8被分为新生代和老年代,新生代又被分为伊甸园区和幸存者区
为什么分区(分代)?
根据对象的存活概率进行分区,将存活时间长的放到老年代,减少扫描垃圾的时间和GC频率,针对分类进行不同的垃圾回收算法,对算法扬长避短。
对象创建内存分配过程
将刚创建的新对象放到伊甸园区,当垃圾回收时将幸存下来的对象放到幸存者0区,再次进行垃圾回收时,将幸存者0区和伊甸园区的对象放到幸存者1中,每次保证有一个幸存者区是空闲的.当一个对象经历15次垃圾回收(被回收对象默认次数是15,可以通过-XX:MaxTenuringThreshold=<N>设置次数)时依然存活,会被放入老年区,或者如果一个对象比较的,也可以直接存放到老年区中.在老年代,相对悠闲,当老年代内存不足时,触发 GC,进行老年代的内存清理. 若老年代执行了 GC 之后发现依然无法进行对象存储,会对整堆进行 GC, 之后依然无法进行对象存储, 就会产生 OOM 异常. Java.lang.OutOfMemoryError:Java heap space
为什么是15次?
在对象头中,它由4位数据去对GC年龄进行记录保存,最大为1111,即15
5.方法区
一个被线程共享的区域,主要存储加载的类字节码、class/method/field 等元数据、static final 常量、static 变量、即时编译器编译后的代码等数据。在物理上与堆是属于同一个空间,但在逻辑上对其进行区分,被称为元空间
6.本地方法栈
Java 虚拟机栈管理 java 方法的调用,而本地方法栈用于管理本地方法的调用. 本地方法栈也是线程私有的. 允许被实现成固定或者是可动态扩展的内存大小.内存溢出方面也是相同的. 如果线程请求分配的栈容量超过本地方法栈允许的最大容量抛出StackOverflowError. 本地方法是用 C 语言写的. 它的具体做法是在 Native Method Stack 中登记 native 方法,在 Execution Engine 执行时加载本地方法库.
四.本地方法接口
1.什么是本地方法?
简单来说,被native修饰的方法就是本地方法,该类方法底层不是由java语言编写,例如c
2.为什么使用本地方法?
与java环境外交互,与硬件设备交互,java属于应用程序语言,没有与硬件设备直接交互的权限
五.执行引擎
将字节码解释/编译为对应平台上的本地机器指令
什么是解释器和编译器?
解释执行虽然效率低,但是在程序开始执行后可以立即投入使用,编译执行虽然将一些热点代码编译后缓存起来,执行效率高,但是需要花费一定时间,所以就采用开始时使用解释器执行,等编译器编译完成后就采用编译执行
六.垃圾回收机制
1.垃圾回收概述
1.1什么是垃圾?
垃圾是指没有被任何引用指向的对象,这个对象就是需要被回收的垃圾.
如果垃圾长时间不回收,这些垃圾对象会一直占用内存空间,导致空间无法被其他对象所使用,直到应用程序关闭,甚至导致内存溢出.
1.2垃圾回收概述
早期的 C/C++时代,垃圾回收基本上是手工进行的,开发人员可以使用 new 关键字进行内存申请,并使用 delete 关键字进行内存释放。这种方式可以灵活控制内存释放的时间,但是会给开发人员带来频繁申请和释放内存的管理负担。倘若有一处内存区间由于程序员编码的问题忘记被回收,那么 就会产生内存泄漏,垃圾对象永远无法被清除,随着系统运行时间的不断增长,垃圾对象所耗内存可能持续上升,直到出现内存溢出并造成应用程序崩溃。

2.垃圾回收的相关算法
2.1垃圾回收标记阶段算法
可达性分析算法是以根(GCRoots)为起始点,按照从上至下的方式搜索被根对象所连接的目标对象是否可达。使用可达性分析算法后,内存中的存活对象都会被根直接或间接连接着,搜索所走过的路径称为引用链(Reference Chain)如果目标对象没有任何引用链相连,则是不可达的,就意味着该对象己经死亡,可以标记为垃圾对象。
哪些对象可以称为活跃对象(GCroots)?
1.虚拟机栈中引用的对象
2.方法区中类静态属性引用的对象
3.所有同步锁synchronized持有的对象
4.java虚拟机内部的引用
基本数据类型对应的class对象,一些常驻的异常对象(nullpointerexception,outofmemoryerror),系统类加载器
2.2垃圾回收阶段算法


3.垃圾回收器
按线程数可以分为 单线程(串行) 垃圾回收器和 多线程(并行) 垃圾回收器按照工作模式分,可以分为 独占式 和 并发式 垃圾回收器。按工作的内存区间分,又可分为 年轻代垃圾回收器 和 老年代垃圾回收器。
3.3CMS回收器
垃圾回收过程初始标记:Stop The World,仅使用一条初始标记线程对所有与 GC Roots 直接关联的对象进行标记。并发标记:垃圾回收线程,与用户线程并发执行。此过程进行可达性分析,标记出所有废弃对象。重新标记:Stop The World,使用多条标记线程并发执行,将刚才并发标记过程中新出现的废弃对象标记出来。并发清除:只使用一条 GC 线程,与用户线程并发执行,清除刚才标记的对象。这个过程非常耗时。并发标记与并发清除过程耗时最长,且可以与用户线程一起工作,因此,总体上说,CMS 收集器的内存回收过程是与用户线程一起并发执行的。
3.4G1回收器

① 初始标记:标记出 GC Roots 直接关联的对象,这个阶段速度较快,需要停止用户线程,单线程执行。② 并发标记:从 GC Root 开始对堆中的对象进行可达新分析,找出存活对象,这个阶段耗时较长,但可以和用户线程并发执行。③ 最终标记:修正在并发标记阶段引用户程序执行而产生变动的标记记录。④ 筛选回收:筛选回收阶段会对各个 Region 的回收价值和成本进行排序,根据用户所期望的 GC 停顿时间来指定回收计划(用最少的时间来回收包含垃圾最多的区域.这就是 Garbage First 的由来——第一时间清理垃圾最多的区块),这里为了提高回收效率,并没有采用和用户线程并发执行的方式,而是停顿用户线程。适用场景:要求尽可能可控 GC 停顿时间;内存占用较大的应用。