JVM
Java Virtual Machine,Java虚拟机
为什么叫Java虚拟机 .顾名思义,我们编写java代码,编译java代码,不是让他在windows上跑,在linux上跑,或者是在MacOS上跑, 而是在Jvm上跑
jvm作用
jvm负责把编译后的字节码转换为机器码
JVM的组织架构
1.类加载器(ClassLoader)
2.运行时数据区(RuntimeData Area)
3.执行引擎(Execution Engine)
4.本地库接口(Native Interface

jvm内部构造
-
类加载部分 : 负责把硬盘上字节码加载到内存中(运行时数据区)
-
运行时数据区: 负责存储运行时产生的各种数据 类信息,对象信息,方法信息...
-
执行引擎: 负责将字节码转为机器码
4.本地方法接口:
5垃圾回收部分
类加载
类加载系统,负责将硬盘上的字节码文件加载到jvm中,生成类的Class对象,存储在方法区.
类就是一个模版
类加载过程
类从被加载到 JVM,分别是加载、验证、准备、解析、初始化。其中验证、准备和解析这三个阶段统称为连接。

1.加载
以二进制文件流进行读取
在内存中为类生成Class对象
2.链接
验证 验证字节码的结构是否正确
准备为类的静态属性进行初始化赋值
static int a = 123; 在准备阶段a的值为0,在初始化阶段才赋值为123
解析 :将类中的符号引用替换成直接引用(符号引用是Class 文件的逻辑符号,直接引用指向的方法区中某一个地址).
3.初始化
初始化阶段主要是为类中静态成员进行赋值.
因为类加载执行完初始化阶段,才说明类加载完成了.
类在哪些情况下会被加载 (类只要被用到了就会被加载)
1调用类中静态成员(变量,方法) 类名,静态变量 类名,静态方法
2new 类的对象
3在类中执行main()
4反射加载类 Class.forName("地址")
5子类被加载
类在以下两种情况下,是不会被加载的
1. 类作为数组类型 Demo demo[] = new Demo[10]; //new 的数组对象 不是Demo对象 2.只是访问类中的静态的常量 System.out.println(Demo.P);//优化 不加载整个类了, 只获取到用到的静态常量
类加载器 (加载类的实现者)
类加载器就是实际负责读取类的功能.
类加载器分类:
1 按照java语言分类,分为 用java语言实现的和非java语言实现
1>引导类加载器
不是用java语言写的使用c/c++实现的,用来加载java系统的中的类 如String
2>扩展类加载器
此加载器,由java语言实现,用于加载java8\jre\lib\ext下的类
3>应用程序加载器
此加载器,由java语言实现,用于加载我们自己开发的程序类
双亲委派机制

当加载一个类时,先让上级的类加载器去加载,优先加载系统中的类,直到启动类加载器,
如果到达顶部的类加载,找到了,那就返回, 如果顶部类加载器找不到,再次向下委派,让子类加载器去加载.
在哪一级找到了,就返回即可,如果向下,都没有找到,那么就抛出类找不到异常.
为什么使用双亲委派机制
优先加载系统中的类,防止我们的类替换了系统中的类.
如何打破双亲委派机制
在 ClassLoader 类中涉及类加载的方法有两个,loadClass(String name), findClass(String name),这两个方法并没有被 final 修饰,也就表示其他子类可以重写. 重写 findClass 方法
我们可以通过自定义类加载重写方法打破双亲委派机制,
运行时数据区------方法区,堆,栈,虚拟机栈,本地方法栈,程序计数器

运行时数据区就是用来存储各种运行时产生的数据
程序计数器
程序计数器,用来记录每个线程执行指令的位置,因为cpu要切换执行
特点:
1程序计数器占用内存小,速度快,
2线程私有,每一个线程都有一个程序计数器用来记录每个线程执行到的位置.
3声明周期与线程相同
4此区域不存在内存溢出的问题
虚拟机栈
虚拟机栈是运行的结构,主要用来执行java语言写的方法
栈是私有的,每个线程运行,都有一个自己的栈空间
栈运行的特点,先进后出/
栈空间是会有内存溢出的(比如说递归调用时)
一个方法被调用后,在栈中称为一个栈帧,保存局部变量,操作数栈,方法返回地址

本地方法栈
本地方法栈中,主要用来运行调用本地方法(native关键字修饰的方法,是由操作系统实现的方法,C/C++)
堆 ---java产生的对象都存储在堆中
特点:
堆空间是虚拟机中空间最大的一块区域,而且大小可以调整,(运行时数据区,除了程序计数器区域不能调整大小,其他四个区域都可以调整大小)
堆空间被所有线程共享
堆空间是会出现内存溢出
堆空间是垃圾回收的重点区域
堆空间区分

1>年轻代/新生代
- 伊甸园区(刚new出来的对象存储)
2.幸存者0 区(from) 3.幸存者1区(to)
2>老年代/老年区
为什么分区
可以把存活时间不同的对象放在不同的区域,对不同的区域进行垃圾回收
频繁的回收新生代,较少的回收老年代 减少及 GC 频率。
对象在堆空间的分配过程
新创建的对象都存储在伊甸园区当垃圾回收时 把伊甸园区的对象转移到幸存者0区,当后面继续回收垃圾时,再把伊甸园区和幸存者0区存活的对象转移到幸存者1区
每次保证有一个幸存者区是空闲的,减少内存碎片 当一个对象经过15次垃圾回收时,依然存活,则就将此对象移动到老年区
还有当一个对象比较大,也可以直接放入老年区
补充:对象的回收的次数最大上线时15 在对象头有4bit位来记录回收次数,
可以通过-XX:MaxTenuringThreshold=<N> 设置次数
设置堆空间的参数
-Xms:初始堆空间内存
-Xmx:最大堆空间内存
-Xmn:设置新生代的大小
-XX:MaxTenuringTreshold:设置新生代垃圾的最大年龄
具体的可以参考官方文档, 这些参数的设置,也是jvm调优的一部分 根据实际使用情况,设置各参数的大小
方法区
特点:
方法区主要存储加载到内存中的类信息,
方法区物理上与堆属于一个空间,但是在一般逻辑上进行区分,称为元空间(方法区)
方法区的大小可以设置 通过-XX:MetaspaceSize=<N>参数方法区的空间一但空间不足,就会触发 FULL GC (整堆收集) ,会影响应用程序的线程 一般情况下,可以把方法区设置较大一些
方法区会出现内存溢出的
方法区存在垃圾回收的
方法区什么时候会被卸载 (方法区的垃圾回收)
1 该类以及该类的子类的所有的对象都不存在了
2 加载该类的加载器不存在了
3 加载该类的CLass对象不存在了
由于条件比较苛刻 一般情况下 可以认为类被加载后就不再释放
本地方法接口
用于对接调用的本地方法
什么是本地方法,
被native修饰的方法就是本地方法
// new Object().hashCode(); //public native int hashCode(); 获取内存地址
// new FileInputStream("").read(); private native int read0() 读硬盘数据
//new Thread().start();// private native void start0(); 把线程注册到操作系统
为什么要使用本地方法
java属于应用程序语言,没有权限直接操作硬件设备 使用本地方法与硬件进行交互
例如获取内存地址,读取硬盘数据,这是就需要对操作系统中的本地方法进行实现
执行引擎
1作用
可以将字节码指令解释/编译成机器码指令
注意提到了两次编译,
第一次编译是指将.java文件编译为.class文件. 属于开发阶段,与运行无关,也可以成为前端编译
第二次变异是在运行时,在虚拟机中通过执行引擎将字节码编译为机器码,称为后端编译
2解释器和编译器
解释器采用不编译,是逐行解释的方式,无需编译,可以实现立即的解释执行的操作,但是效率略低
编译器将字节码整体编译后执行,编译需要花费一段时间,但是编译后的执行效率高
3为什么这样设计 ---Java 是半编译半解释型语言
解释执行,虽然效率低,但是在程序开始后,可以立即投入使用
编译执行,虽然将一些热点代码编译后缓存起来执行那个效率高,但是花费时间
所以采用开始时,使用解释器执行,等编译完成后采用编译执行
垃圾回收
相关概念
1.什么是垃圾
在运行过程中,如果一个对象没有被任何引用所指向,那么这个对象就称为垃圾对象.
如果垃圾对象不清理,后来的对象就可能没有地方存放,进而导致内存溢出.
2.早期和现代的垃圾回收
早期c/c++都是手动的申请和释放内存,
好处: 可以精确的管理内存, 用的时候申请,不用的时候可以立即释放.
缺点: 给程序员带来负担, 一旦忘记释放,一直存在.
现代java,python,c#....语言都采用自动垃圾回收
好处: 解放了程序员,不需要关心什么时候释放垃圾对象
缺点: 降低了程序员对内存管理的能力
3.垃圾回收的区域
运行时数据区中的堆和方法区是回收的区域
堆是回收的重点区域
频繁的回收新生代
较少回收老年代
基本不回收方法区(在full gc 整堆回收时 ,才会对方法区进行回收)
4.内存溢出与内存泄漏
内存溢出: 内存不够用了,还继续有新的对象产生,此时回报出内存异常错误.
内存泄漏: 一些对象已经不再使用了,但是垃圾收器,却不能回收它,例如数据库连接对象,网络SOcket对象,
还有IO流对象等,提供close()进行断开,但是没有调用,那么垃圾回收器认为此对象还在使用中.这些对象就悄悄的占用着内存资源, 这种现象称为内存泄漏, 久而久之会引发内存溢出问题.
垃圾回收相关算法
标记阶段算法
标记阶段主要就判断哪些对象是垃圾对象,把没有引用指向的对象标记出来,
在垃圾回收时进行回收.
标记阶段提到两种算法:
1.引用计数算法(目前没有被使用)
在对象中提供一个计数器,有引用指向对象,那么计数器就加一,当计数器为0时,表名该对象没有被任何引用指向.
实现简单,
但是有一个致命的缺陷,无法解决循环引用问题.
多个对象之间相互引用,计数器不为0 ,但是此时这几个对象,与外界已经没有联系了,使用不到了,
他们相互又有引用,垃圾收集器不能回收他们,会造成内存泄漏问题.
2.可达性分析算法
可达性分析算法,可以解决引用计数算法中的循环引用问题.
思想是从一些活跃对象开始查找,与活跃对象相连接的对象,都是被使用的,
如果没有与与这些活跃对象相连接的对象,就可以判断为垃圾对象
哪些对象可以称为活跃对象(GCRoots):
1.虚拟机栈中引用的对象 比如:各个线程被调用的方法中使用到的参数、局部变量等。
2.方法区中类静态属性引用的对象,比如:Java 类的引用类型静态变量
3.所有被同步锁 synchronized 持有的对象 4.Java 虚拟机内部的引用。 基 本 数 据 类 型 对 应 的 Class 对 象 , 一 些 常 驻的异常对象(如:NullPointerException、OutofMemoryError),系统类加载器。
finalize机制
java中的Object类中提供一个finalize()方法,
这个方法在对象被判定为垃圾后,在垃圾回收器回收这个对象时,会调用finalize().
finalize()规定只会被调用一次. 假如一个对象第一被回收时,调用finalize(),在finalize()中又被使用到了,复活了,
当第二次再被判定为垃圾,回收时,不再调用finalize()方法.
我们可以重写finalize(),去执行一些对象回收前的最终操作, 但是我们不要自己主动的去调用该方法.
还有此方法中内容需要慎重,否则,影响垃圾收集器的性能.
由于finalize()存在
把对象分为三种状态:
可触及: 使用中的对象,没有被判定为垃圾
可复活: 被判定位垃圾对象,但是还没有调用finalize(),有可能在finalize()中复活.
不可触及: 第二次被判定为垃圾,finalize()已经调用过了.
回收阶段算法
标记-复制算法:
可以有多块内存,每次回收时,将正在使用的内存块中的存活对象,复制到另一块未使用的内存块中,
然后清除原来的内存块.
特点: 使用到多块内存
回收时需要移动对象,适合内存小,存活对象少,垃圾对象多的场景 (适合新生代)
回收后,内存碎片少.
标记-清除算法:
只需要一块内存空间,把存活对象是不用进行移动,直接清除垃圾对象.
特点: 只需要一块内存,
不需要移动存活对象, 只清除垃圾对象
回收后,会产生内存碎片问题
适合于存活对象多且对象较大的场景 (适合老年代)
标记-压缩算法
标记压缩算法,就是在标记清除算法的基础上,对存回对象进行移动,将存回对象移动到内存一端,按顺序排放,
清除边界之外的区域,
是一种移动式的垃圾回收算法,回收后,内存中没有内存碎片
适合老年代回收
分代收集
年轻代的对象,存活的少,垃圾多,使用标记复制算法进行回收
老年代的对象大,且存活时间长,采用标记清除和标记压缩算法进行回收.
优化垃圾回收效率
垃圾收集器
垃圾收集器是jvm中垃圾回收的实现者.
垃圾收集器种类很多,不同的虚拟机中可以使用不同的垃圾收集器.
垃圾收集器分类:
从垃圾收集器线程数量上划分:
单线程, 垃圾收集器中,只有一个线程在执行垃圾回收操作
多线程,垃圾收集器中,有多个线程在执行垃圾回收操作
从工作模式上分为:
独占式的: 当垃圾回收线程在执行时,其他用户线程暂停(把用户线程暂停的现象称为stw)
并发式的: 垃圾回收线程和用户线程可以同时执行, 减少了用户线程暂停的次数和时间
按照工作内存分为:
新生代垃圾收集器
老年代垃圾收集器
jdk8支持一下几种垃圾回收器
CMS(Concurrent Mark Sweep) 并发标记清除
首创了垃圾回收线程和用户线程并发执行, 追求低延时.
初始标记阶段 : 独占式的
并发标记阶段: 并发式的
重新标记阶段: 独占式的
并发清除阶段: 并发式的
G1(Garbage-First)收集器
首先G1垃圾收集器,适用于整堆收集,不再区分新生代,老年代
适用于服务器场景.
G1垃圾收集器,将每个区域再划分为更小的区域, 例如将伊甸园区划分为多个小区域,
优先回收垃圾对象较多的区域, 故得名Garbage-First--垃圾优先.
也是支持并发执行
查看并设置垃圾收集器
-XX:+PrintCommandLineFlags -version 打印jdk中使用的垃圾收集器信息
-XX:+UseG1GC 设置垃圾收集版本