平台无关 一次编译到处运行
- javac编译成class字节码 虚拟机加载class文件,将其转换为当前计算机可执行的机器码进行执行
- javap -c xxx.class
- jvm屏蔽了计算机操作系统的信息
jvm如何加载.class文件
通过类加载器去找到.class文件最终通过反射来创建(只要知道class 能够获取类的所有东西)
classloader种类
- BootstrapClassLoader C++编写 加载核心库 java.*
- ExtClassLoader java编写 加载javax.*
- AppClassLoader java编写 加载程序路径下的
- 自定义classloader 定制化加载
java虚拟机内存模型
- classloader,类加载器
- runtime data area,运行时数据区(堆,方法区,虚拟机栈,本地方法栈,程序计数器)
- execution engin,对命令进行解析
- native interface 本地接口,不同语言的原生库
jvm内存模型的三大特性
- 原子性:要么执行 要么不执行
- 可见性:一个线程对共享变量的修改,另一个线程能够看得到(具体的说:在变量修改后将新值同步回主内存,主要有两种实现方式,一是volatile,被volatile修饰的变量发生修改后会立即刷新到主内存;二是使用Synchronize或者lock,当一个变量unlock之前会将变量的修改刷新到主内存中)
- 有序性:程序执行的顺序按照代码的先后顺序执行。(具体的说:在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序不会影响单线程的执行结果,却会影响多线程并发执行的正确性。主要有两种方式确保有序性:volatile 和 Synchronize 关键字,volatile是通过添加内存屏障的方式来禁止指令重排序,也就是重排序是不能把后面的指令放到内存屏障之前执行;Synchronize是保证同一时刻有且只有一个线程执行同步代码,类似于串联顺序执行代码)。
垃圾回收机制
判断对象是否为垃圾的算法
- 引用计数算法
- 可达性分析算法
引用计数算法
判断对象的引用数量
- 通过判断对象的引用数量来决定对象是否可以被回收
- 每个对象实例有一个引用计数器 被引用+1,完成引用-1
- 任何引用技术为0的对象实例可以被当作垃圾收集
优点:执行效率高,程序影响小
缺点:无法检测出循环引用的情况,导致内存泄露
ps:内存溢出 内存泄露
可达性分析算法
通过判断对象的引用链是否可达来决定对象是否可以被回收
- 原理是离散数学的图论 通过一系列的GcRoot作为根节点进行搜索,搜索路径就是引用链 当一个对象从GcRoot没有任何引用链相连,就说这个对象是不可达的,这个对象不可用,标记为垃圾
- 可以作为GcRoot的对象
- 虚拟机栈中引用的对象
- 方法区常量引用对象
- 方法区中类静态属性引用的对象
- 本地方法栈中JNI(Native)的引用对象
- 活跃线程的引用对象
垃圾回收算法
标记清除算法
- 标记:从根集合进行扫描,对存活对象进行标记(判断是否可达)
- 清除:对堆内存从头到尾进行线性遍历,回收不可达对象内存(将没有被标记的对象清除掉)
- 缺点 碎片化
- 适用于老年代
复制算法
原理:
- 分为对象面和空闲面
- 对象在对象面上创建
- 存活的对象被从对象面复制到空闲面
- 将对象面多有对象内存清除
优缺点:
- 解决碎片化的问题
- 顺序分配内存,简单高效
- 适用于对象存活率低的场景
- 对象存活率高的时候不适用 会浪费50%的空间
- 适用于新生代
标记整理算法
原理:
- 标记:从根集合进行扫描,对存活对象进行标记(判断是否可达)
- 清除整理:移动所有存活对象按照内存地址次序依次排列,然后末端内存地址以后的内存全部回收
优缺点:
- 避免内存的不连续
- 不用设置两块内存交换
- 适用于存活率高的场景
- 适用老年代
分代收集算法(常用垃圾收集器使用的)
- 垃圾回收算法的组合拳
- 按照对象生命周期的不同划分区域以采用不同的垃圾回收算法
- 目的:提高JVM的回收效率
- 主流的收集算法
jvm内存空间
- 在1.7及以前分为 老年代 年轻代 永久代
- 从1.8开始 没有永久代了
- 年轻代存活率低 采用复制算法 老年代采用标记清除算法、标记整理算法
年轻代 1/3内存空间
- 组成
- Eden区间 80% 新创建的对象都在这里面
- 两个Survivor区 各占10%
- 作用 尽可能快速的收集掉那些生命周期短的对象
- 收集过程
- 当Eden区满了 触发一次MinorGc,会将存活对象复制到S0,计数加1,清除Eden区所有空间。
- 当Eden又满了,又一次触发MinorGc,会将Eden区和S0存活对象复制到S1,计数加1,清除Eden和S0
- 再一次满了 又会将Eden和S1复制到S0,计数加1
- 怎样晋升到老年大
- 当年龄达到一定数(默认15)将会存入老年代
- 在年轻代survivor装不下的对象也会直接进入老年代
- 有对应的参数
老年代 2/3
- 存放生命周期长
- 收集算法 标记清理、标记整理
- 老年代垃圾回收
- FullGc和MajorGc
- FullGc比MinorGc慢 执行频率低(因为老年代的对象一般存活率都比较高)
- FullGc触发条件
- 老年代空间不足
- 永久代空间不足(1.8之前)
- CMSGC 收集出现异常
- MinorGc晋升到老年代的平均大小大于老年代剩余空间
- 调用System.gc
- 使用RMI来进行RPC或管理的JDK应用,每小时执行一次Full GC
stop the world
- 执行Gc而停止应用程序的执行
- 任何一种Gc都会发生
- 多数Gc通过优化减少发生时间提高程序性能,高吞吐 低停顿
SafePoint 安全点
- 分析过程中对象引用关系不会发生变化的点
- 产生安全点的地方:方法调用;循环跳转;异常跳转
- 安全点的数量需适中
jvm运行模式
- Server 重量级虚拟机 启动慢 稳定和快
- Client 轻量级 启动快
常见垃圾收集器 hospot
年轻代常见垃圾收集器
1: Serial收集器(-XX:+UseSerial 复制算法)
- 单线程收集,进行垃圾收集时,必须暂停所有工作线程
- 简单高效 client模式下默认的年轻代收集器
- ParNew收集器(-XX:+UseParNewGC 复制算法)
- 多线程收集,其余行为、特点和Serial收集器一样
- 单核情况下效率不如Serial,多核下执行才有优势
- 关注系统停顿时间
- Parallel Scavenge收集器(-XX:UserParallelGC 复制算法)
- 系统吞吐量 = 运行用户代码时间/(运行用户代码时间+垃圾收集时间)
- 和ParNew类似
- 关注系统吞吐量
- Server模式下默认的年轻代收集器
老年代常见垃圾收集器
- Serial Old收集器(-XX:+UseSerialOldGC b标记-整理算法)
- 单线程收集,进行垃圾收集时,必须暂停所有工作
- 简单高效,Client模式下默认的老年代收集器
- Parallel Old收集器(-XX:+UseParallelOldGC 标记-整理算法)
- 多线程,吞吐量优先 和Parallel Scavenge收集器配合使用
- CMS收集器(-XX:+UseConcMarkSweepGC 标记-清除算法)
- 收集线程和应用程序线程几乎同步
- 老年代主要垃圾收集器
步骤
-
- 初始标记:stop-the-world
-
- 并发标记:并发追溯标记,程序不会停顿
-
- 并发预清理: 查找执行并发标记阶段从年轻代晋升老年代的对象
-
- 重新标记:暂停虚拟机,扫描SCM堆中剩余的对象
-
- 并发清理:清理垃圾对象,程序不会停顿
- 并发重置:重置CMS收集器的数据结构
- G1收集器(-XX:+UseG1GC,复制+标记-整理算法)
- Garbage First
- 将整个java堆内存划分成多个大小相等的Region
- 年轻代和老年代不在物理隔离
GC相关
- Object的finalize()方法
- 给与对象最后一次重生机会
强引用、软引用、弱引用、虚引用
- 强引用
- 最普遍的引用:Object obj = new Object();
- 抛出OOM异常终止程序也不会回收强引用的对象
- 通过将对象设置为null来弱化引用,使其被回收
- 软引用(Soft Reference)
- 对象处在有用但非必须的状态
- 只有当内训空间不足时,GC会回收该引用对象的内存
- 可以用来实现高速缓存
String str = new String("java");
SoftReference<String> softRef = new SoftReference<String>(str);
- 弱引用(Weak Reference)
- 非必须的对象,比软引用更弱一些
- GC时会被回收
- 被回收的概率也不是太大,因为GC线程优先级比较低
- 适用于引用偶尔被使用且不影响垃圾收集的对象
String str = new String("java");
WeakReference<String> weakReference = new WeakReference<>("str");
- 虚引用(PhantomReference)
- 不会决定对象的生命周期
- 任何适合都可能被垃圾收集器回收
- 跟踪对象被垃圾收集器回收的活动,起哨兵作用
- 必须和引用队列ReferenceQueue联合使用
String str = new String("java");
ReferenceQeueu qeueu = new ReferenceQeueu();
PhantomReference ref = new PhantomReference(str,qeueu);
- 总结对比
强>软>弱>虚
引用类型 | 被垃圾回收的时间 | 用途 | 生存时间 |
---|---|---|---|
强引用 | 从来不会 | 对象的一般状态 | jvm停止运行时终止 |
软引用 | 内存不足时 | 对象缓存 | 内存不足时 |
弱引用 | 在垃圾回收时 | 对象缓存 | GC运行后终止 |
虚引用 | Unknown | 哨兵、标记 | Unknown |
ReferenceQueue 引用队列 无存储结构