Java不是最强大的语言,但是JVM是最强大的虚拟机
虚拟机
Virtual Machine:
- 含义:就是一台虚拟的计算机,它是一款软件,用来执行一系列虚拟计算机指令;
- 分类:系统虚拟机和程序虚拟机
- 系统虚拟机:完全是对无力计算机的仿真;
- 程序虚拟机:专门执行单个计算机程序而设计;
Java虚拟机
Java Virtual Machine
- 跨语言(scale,grovery),跨平台;
- 作用:就是二进制字节码的运行环境
- 特点:
- 一次编译,到处运行;
- 自动内存管理;
- 自动垃圾回收功能;
- JVM是运行在操作系统上,没有与硬件直接交互;
- JDK -> JRE -> JVM
- Hotspot Vm
- 虚拟机是目前市面上高性能虚拟机的代表作之一;
Java代码执行流程
- Java程序–编译–>字节码文件
- Java源码–生成–>Java编译器
- todo:流程图
jvm 架构模型
Java编译器输入的指令流基本上是一种基于栈的指令集架构,另外一种指令集架构则是基于寄存器的指令集架构;
- 基于栈式架构的特点:
- 设计和实现更简单,适用于资源受限的系统;
- 避开寄存器的分配难题:使用零地址指令方式分配;
- 指令流中的指令大部分都是零地址指令,其执行过程依赖于操作栈,指令集更小,编译器更容易实现;
- 不需要硬件支持,可移植性更好,更容易实现跨平台;
- 基于寄存器的指令集架构:
- 完全依靠硬件,可以移植性查;
- 性能优秀和执行效率更高;
内存结构
- 运行时数据区:
- 执行引擎
- 垃圾回收
类加载
- 类加载子系统:
- 负责从文件系统或者网络中加载Class文件,class文件在文件开头要求有特定的标识;
- 加载的类信息存放于方法区;除了类的信息外,方法区还存放运行时常量信息,可能还包括字符串字面量和数字常量(这部分常量信息是Class文件中常量池部分的内存映射)
- 类装载器ClassLoader
- 类加载过程
- 加载Loading
- 通过一个类的全限定名获取定义此类的二进制字节流
- 链接(Linking)
- 验证(Verify)
有效标识:CA FE BA BE
目的:为了保证文件的字节流符合当前虚拟机要求;
验证方式:文件格式验证(),元数据验证,字节码验证,符号引用验证; - 准备(Prepare)
1. 为初始化变量赋默认值
2. 不包含final 修饰的static,因为final在编译的时候就分配了,准备阶段会显示初始化;
3. 这里不会为实例变量初始化;类变量会分配在方法区中,而实例变量会随着对象一起分配到Java堆中
c. 解析(resolve) - 初始化(init)
执行类构造器方法()的过程;
此方法是javac编译器自动生成的;
()不同于类构造器;
一个内部类至少存在一个类的构造器;
虚拟机必须保证一个类的()方法在多线程下能被同步加载;
- 类加载器的分类
- JVM支持两个类型的加载器:
引导类加载器(Bootstrap ClassLoader):C和C++编写
自定义加载器(UserDefine ClassLoader): - 虚拟机自带的加载器:
启动类加载器(BootStrap ClassLoader):C 和C++编写
扩展类加载器Extention ClassLoader:Java语言编写,派生于CLassLoader类; - 应用程序类加载器(系统类加载器,APPClassLoader):该类加载是程序中默认的类加载器
- 用户自定义加载器:
- 为什么使用?
- 隔离加载类
- 修改类加载的方式;
- 修改加载源;
- 防止源码泄露;
- 如何自定义加载类:
- 继承抽象类java.lang.ClassLoader
- jdk1.2之后,建议把自定义的逻辑卸载findClass()中;
除启动类加载器之外,其他启动类都继承自抽象类ClassLoader;
- 加载,加载之后进行验证,验证是否为.class文件,验证之后进行准备,准备之后进行数据解析,解析之后初始化,初始化之后开始使用,使用完成之后卸载;加载:类加载器加载.class文件;加载过程最终的产物是在堆的class对象;
双亲委派机制
工作原理:
1. 如果一个类加载器收到了一个类加载请求,他并不会自己先去加载,二十把这个请求委托给父类的加载器去执行;
2. 如果父类加载器还存在其谷类加载器,则进一步向上委托,依次地柜,请求最终将到达顶部的启动类加载器
3. 如果父类可以完成类加载器任务,就成功返回,如果父类加载器无法完成此任务,子类加载器才会尝试加载;
为什么使用:
区域划分:
- 线程共享区:
- 堆:存放对象实例和数组 --> jvm优化技术,逃逸分析(-XX:+DoEscapeAnalysis)
- 方法区:
- 线程私有:
- 程序计数器,
- 虚拟机栈,
- 本地方法栈
引用
引用:
- 强引用:
类似于Object object = new Object(); 强引用都不回被回收; - 软引用:
Softrefence类实现软引用;在系统要发生内存泄漏之前,将会把这些对象进回收范围之中进行二次回收; - 弱引用:
weakReference类实现;对象只能生存到下一次垃圾回收之前;无论内存是否够用都要回收; - 虚引用:
PhantomRefrence实现;无法通过一个虚引用获取一个实例;必须搭配ReferenceQueue使用,被标为虚引用的对象,在发生GC时会被放进队列,然后回收
引用类型 | 被回收时间 | 用途 | 生存时间 |
强引用 | 从来不会 | 对象的一般状态 | JVM停止运行 |
软引用 | 内存不足时 | 对象缓存 | 内存不足时 |
弱引用 | jvm垃圾回收 | 对象缓存 | gc运行后 |
虚引用 | 未知 | 未知 | 未知 |
记忆:
强 —— 有地位,不商量
弱 —— 无地位,直接收
软 —— 留情面,量满收
虚 —— 存队列,直接收
值传递和引用传递
1.值传递:你有一把钥匙,当你的朋友想要去你家的时候,如果你直接把你的钥匙给他了,这就是引用传递。这种情况下,如果他对这把钥匙做了什么事情,比如他在钥匙上刻下了自己名字,那么这把钥匙还给你的时候,你自己的钥匙上也会多出他刻的名字。
2.引用传递:你有一把钥匙,当你的朋友想要去你家的时候,你复刻了一把新钥匙给他,自己的还在自己手里,这就是值传递。这种情况下,他对这把钥匙做什么都不会影响你手里的这把钥匙。 但是,不管上面哪种情况,你的朋友拿着你给他的钥匙,进到你的家里,把你家的电视砸了。那你说你会不会受到影响?
GC
- 如何判定对象是否为垃圾?
- 引用计数法(jvm一般不使用)
- 逻辑:
- 在对象中引用一个程序计数器
- 对象被引用,计数器+1
- 引用失效,计数-1
- 统计结果为0的对象作为结果
- 缺点:难以解决对象之间互相循环引用的问题;
- 应用:
- 微软COM计数
- Python
- 针对缺陷:
- 问题:如果A引用B,B引用A,那么这两个对象是否都会被回收;
- 关键不是在于A,B之间是否有引用,而是A,B是否可以一直向上追溯到GC Roots。如果与GC Roots没有关联,则会被回收,否则将继续存活。
- 可达性分析性算法(java使用)
- 逻辑:
- 通过GC Roots作为起点
- 通过起点向下遍历,走过的路径作为引用链;
- 仅判定引用链包含的对象作为可达对象;
- 哪些可以作为GC Roots
- 虚拟机栈(栈帧中的本地变量表)中引用的对象
- 方法区中类静态属性引用的对象(一般指被static修饰的对象,加载类的时候就加载到内存中)
- 方法区中常量引用的对象
- 本地方法栈中JNI(即一般说的native方法)中引用的对象;
- 哪些区域需要回收?
- 无需GC
- 范围:程序计数器、虚拟机栈、本地方法栈
- 特点:随线程生,随线程死
- 原因:编译器即可知道内存占用大小,所以内存的回收和分配都具有确定性
- 需要GC
- 范围:虚拟机堆、静态区->常量池
- 原因:运行期动态创建,内存的分配和回收都有不确定性;
- 常用GC算法
- 标记-清除算法(mark-sweep)
直接对垃圾对象进行标记,然后清除;
缺陷:会产生很多的内存碎片; - 标记-复制算法(mark-copy)
将内存分为两半,总是保留一部分空着,将另一半存活的对象复制到另一半,然后左侧全部清除;
缺陷:解决了内存碎片问题,但是内存浪费严重; - 标记-压缩算法(mark-compoact)
将垃圾对象清理后,将剩下的存活对象进行整理挪动,保证空间连续;
缺陷:降低GC效率 - 分代收集法(generation-collect)
- 垃圾回收器
- Serial收集器
- ParNew收集器
- Parallel收集器
- CMS收集器(主流)
- G1收集器(主流)
5. jvm 优化
1. 堆设置
1. -Xms 堆内存的初始大小,默认为物理内存的1/64
2. -Xmx 堆内存的最大大小,默认为物理内存的1/4
3. Xmn 堆内新生代的大小。通过这个值也可以得到老生代的大小:-Xmx减去-Xmn
4. -Xss 设置每个线程可使用的内存大小,即栈的大小。
5. -XX:NewRatio:设置新生代和老年代的比值。如:为3,表示年轻代与老年代比值为1:3
6. -XX:SurvivorRatio:新生代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:为3,表示Eden:Survivor=3:2,一个Survivor区占整个新生代的1/5
7. -XX:MaxTenuringThreshold:设置转入老年代的存活次数。如果是0,则直接跳过新生代进入老年代
8. -XX:PermSize、-XX:MaxPermSize:分别设置永久代最小大小与最大大小(Java8以前)
9. -XX:MetaspaceSize、-XX:MaxMetaspaceSize:分别设置元空间最小大小与最大大小(Java8以后)
2. 收集器设置
1. -XX:+UseSerialGC:设置串行收集器
2. -XX:+UseParallelGC:设置并行收集器
3. -XX:+UseParalledlOldGC:设置并行老年代收集器
4. -XX:+UseConcMarkSweepGC:设置并发收集器
3. 垃圾回收统计信息
1. -XX:+PrintGC
2. -XX:+PrintGCDetails
3. -XX:+PrintGCTimeStamps
4. -Xloggc:filename
4. 并行收集器设置
1. -XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
2. -XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
3. -XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
5. 并发收集器设置
1. -XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
2. -XX:ParallelGCThreads=n:设置并发收集器新生代收集方式为并行收集时,使用的CPU数。并行收集线程数。
s