【JVM学习】垃圾收集器与内存分配策略
垃圾收集器与内存分配策略
对象已死?(判断对象是否可以回收)
引用
-
reference类型的数据中存储的数值代表的是另一块内存的起始地址
- 强引用:代码中的引用赋值 例如 new对象;意义:对象不会被回收
- 软引用:还有用非必须的对象;意义:在系统将要发生内存溢出前回收
- 弱引用:非必须对象;意义:每次垃圾回收时都会被清理
- 虚引用:最弱的一种引用;意义:可以让一个对象被回收时收到一个系统通知
引用计数算法
- 在对象中添加一个引用计数器,被引用加一,引用失效减一,为零就不会再使用可以回收
- 优点:原理简单,效率高
- 缺点:实用性不强,例如 很难解决对象间的互相循环引用的问题
可达性分析算法
-
判断某个对象到GC Roots间有没有引用链相连,如果没有就说明对象不可达、不再使用可以回收
-
GC Roots
- 虚拟机栈的本地变量表中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI(Native方法)引用的对象
- java虚拟机内部的引用,如基本数据类型对应的Class对象,常驻的异常对象,类加载器
- 同步锁持有的对象
- 反应虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等
生存还是死亡(判断对象是否真的能被回收)
- 至少要经历两次标记(判断),如果第一次被标记成不可达,但是在第二次标记前又与GC Roots建立了链接就不会被清理
回收方法区
- 《java虚拟机规范》不强制方法区实现垃圾收集
- 方法区垃圾收集性价比比较低
- 主要回收废弃的常量和不再使用的类型(类型指加载到内存中的Class)
垃圾收集算法
引用计数式垃圾收集(直接垃圾收集)
追踪式垃圾收集(间接垃圾收集)(主流)
-
分代收集理论
设计原则:收集器应该将java堆划分出不同区域,然后将回收对象依据其年龄分配到不同的区域之中存储
-
弱分代假说:绝大多数对象都是朝生夕死的
-
强分代假说:熬过越多次垃圾收集过程的对象越你难以死亡
-
分代
- 新生代
- 老年代
-
跨分代引用假说:存在相互引用关系的对象应该倾向于同生同死,跨代引用占比较少。
-
存在问题:当扫描新生代对象时如果有跨代引用还需要扫描整个老年代,成本较高
- 解决办法:新生代建立一个全局数据结构(记忆集),这个结构将老年代划分成若干小块,标识出老年代的哪一块内存存在跨代引用。Minor GC时只有包含了跨代引用的小块内存里的对象才会被加入到GC Roots进行扫描,而不用扫描整个老年代
-
-
-
标记-清除算法:标记后清除被标记的对象
- 缺点:1、执行效率不稳定(受要标记的目标对象数量的限制);2、内存空间碎片化(产生不连续的内存碎片)
-
标记-复制算法:将内存划分为大小相等的两块,每次只用其中一块,当内存用完了就将存活对象复制到另一块,然后将已使用的内存一次清理掉
- 缺点:空间浪费太严重
- 新生代优先采用此算法,而老年代不会使用这种方法,因为新生代98%对象都是朝生夕死的,这种情况实现简单、效率高
-
标记-整理算法:先标记,让存活对象都移动到内存一端,然后清理掉边界以外的内存
经典垃圾收集器
Serial收集器
- 单线程收集器
- 优点:简单高效、内存消耗最小
- 缺点:工作时必须“Stop The World”,用户体验差
ParNew收集器
- Serial收集器多线程并行版本
- HotSpot虚拟机中第一款退出历史舞台的垃圾收集器,只能和CMS配合使用而CMS将会替代CMS
Parallel Scavenge收集器
- 多线程,基于标记-复制算法
- 设计目标:达到一个可控的吞吐量(也就是控制垃圾回收所占用的时间)
Serial Old收集器
- Serial的老年代版本
- 单线程,基于标记整理算法
Parallel Old收集器
- Parallel Scavenge收集器老年代版本
- 多线程,基于标记-整理算法
CMS收集器
-
目标:为了获取最短回收停顿时间
-
多线程,基于标记-清除算法
-
缺点:1、处理器资源敏感,会占用一部分资源导致程序变慢,总吞吐量降低。2、无法处理浮动垃圾,可能导致full gc发生。3、无法处理空间碎片,只能依靠full gc
浮动垃圾:CMS标记和清理阶段用户线程会同时运行并产生新的垃圾,因为有些垃圾在标记后产生所以本次无法清理,只能等到下次。
Garbage First收集器(G1)
-
将堆内存划分为多个大小相等的独立区域(Region),每个区域都可以根据需要扮演新生代的Eden空间、Survivor空间或老年代空间,每次回收时G1会判断那块回收性价比最高,收益最大;Region中设有Humongous区域,用来存储大对象(超过Region一半的对象),而超级大对象会被存放在多个连续的Humongous区域
-
与CMS比较
- 优势:1、不会产生垃圾碎片;2、创新性设计带来较多红利:可以指定最大停顿时间,分区域的内存布局,按收益动态确定回收集
- 弱势:1、占用内存多、负载率高
基础故障处理工具
jps:虚拟机进程状况工具,主要用来查看进行的唯一ID
jstat: 虚拟机统计信息监视工具,主要用于查看虚拟机各种运行状态信息
jinfo:java配置信息工具,主要使用-sysprops 打印System.getProperties()的内容
jmap:java内存映像工具,主要用于生成堆转储快照
jhat:虚拟机堆转储快照分析工具,图形化工具更适合这个工作 所以这个命令用处不大
jstack:java堆栈跟踪工具,主要用于生成虚拟机当前时刻线程快照,例如可以查看没有相应的线程在后台干什么
内存回收策略
在经典分代设计下,新生对象通常会分配在新生代中,少数情况下也可能直接分配在老年代
对象优先在新生代Eden区域分配
大对象直接进入老年代
长期存活对象进入老年代
- Eden中的对象经过一次GC仍然存活并且能被Survivor容纳就会被转移到Survivor中,并将对象年龄设置为1,对象在Survivor中每熬过一次GC就增加一岁,年龄增加到15(默认 可以设置)时进入老年代
动态对象年龄判定
- HotSpot虚拟机并不是永远等到对象年龄达到标准才晋升到老年代,如果Survivor空间中的相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就直接进入老年代
选择合适的垃圾收集器
垃圾收集器工作内容
- 垃圾收集、堆内存的管理与布局、对象的分配、与解释器的协作、与编译器的协作,与监控子系统的协作等等
应用只要运行数分钟甚至数秒,只要虚拟机能正确分配内存,在堆耗尽之前就会退出
- Epslion收集器,不进行垃圾回收,但负载极小
收集器的权衡
-
应用程序的关注点
- 吞吐量
- 延迟
- 内存占用
虚拟机及垃圾收集器日志
- 1、查看GC基本信息:jdk9以前 -XX:+PrintGC,9以后 -Xlog:gc
- 2、查看GC详细信息:9以前 -XX:+PrintGCDetails,9以后 -X-log:gc*
- 3、查看GC前后堆、方法区可用容量变化:9以前-XX:+PrintHeapAtGC,9以后 -Xlog:gc+heap=debug
- 4、查看GC过程中用户线程并发时间以及停顿的时间:9以前-XX:Print-GCApplicationConcurrentTime 及 -XX:+PrintGCApplicatiionStoppedTime,9以后 -Xlog:safepoint
- 只列出一些常用的命令
本文地址:https://blog.csdn.net/qq_43005544/article/details/110282695