【2019秋冬】【JVM】深入理解Java虚拟机 第三章

哪些内存需要回收?
什么时候回收?
怎么回收?

程序计数器,虚拟机栈,本地方法栈都为线程私有
需要处理的是Java堆和方法区

判断对象是否已死
1.引用计数法
给对象添加一个引用计数器,当有引用时加一,引用失效时减一,当计数器为0时可以回收
实现简单,效率高,但难以解决循环引用问题
2.可达性分析算法
在GC root向下搜索,不和GC Root相连的就是可回收
GC Root可以是:
虚拟机栈(栈帧中的本地变量表)中引用的对象
方法区中类静态属性引用的对象
方法区中常量引用的对象
本地方法栈中的native引用的对象
解释:
1.是指虚拟机栈中引用到的对象
2.是指static
3.是指final
4.是指native方法

jdk1.2前对引用的定义:如果reference类型的数据中存储的数值代表另一块内存的起始地址,就称这块内存代表着一个引用
1.2后,对引用进行了补充,分为四种
强引用:
只要强引用在,就不会被回收
软引用:
还有用但并非必需的对象,在系统将要发生内存溢出之前,会对软引用对象列进回收范围进行二次回收,如果回收后内存还不够,会异常
弱引用:
描述非必须对象,比软引用更弱,只能生存到下一次垃圾回收之前,无论内存是否够用,都会回收
虚引用:
最弱的引用关系,是否被回收与引不引用无关,无法通过虚引用获得对象实例

对象死亡,需要两次标记
第一次:
在可达性分析后,没有与GC Root相连,被标记一次,并进行筛选
筛选条件:是否执行finalize()方法,执行过了就死亡,没执行过放入F—Queue队列中
进入队列后,虚拟机会将队列中的对象进行二次小规模标记,如果对象可以重新连接上其他对象,就可以不被死亡
finalize()是否执行判定:如果finalize()没有被覆盖,或者已经被调用过,就是没必要执行,一个对象的finalize只能被调用一次

方法区回收
方法区主要回收两个部分:
常量:没有任何对象引用该常量,就是废弃的常量
类:满足三个条件即是无用类
1.该类所有的实例都被回收(Java堆中不存在该类的任何实例)
2.该类的ClassLoader已经被回收
3.(该类的java.lang.class对象没有在任何地方被引用)无法通过反射访问该类的方法

垃圾收集算法
1.标记—清除算法(老年代)
标记出所有要回收的对象,标记完成后统一回收
效率低,空间零碎
2.复制算法(新生代)
将内存划分为相等的两块,当一块内存用完了,就把活着的对象复制到另一块上,将当前块清空
目前都按8:1:1划分为Eden,Survivor,Survivor三块,每次使用Eden和一块Survivor,当快满时,将存活的对象赋值到另一块Survivor
当Survivor不够用时,依赖老年代进行分配担保
3.标记—整理算法(老年代)
标记所有对象,让所有存活对象向一端移动,然后直接清除掉边界意外的内存
4.分代收集算法
分新生代老年代进行回收,不同代采用不同回收算法

OopMap,安全点,安全区
虚拟机用OopMap存储哪些地方存放着引用对象,类加载时完成,可以快速准确完成GC Root 枚举
虚拟机在安全点位置记录OopMap信息,在安全点可以进行GC
在GC发生时让所有线程都到达安全点方法:
抢先式中断:GC时,把所有线程中断,如果该线程不在安全点,恢复并让其到达安全点
主动式中断:GC需要中断时,在安全点处设置一个标志,各个线程主动访问标志,发现标志为真就开始挂起
安全区是一段代码中,引用关系不会变化,可以随时开始GC,可以看成扩展的安全点

并行:多个线程同时工作,但其他线程处于等待
并发:多个线程同时执行(不一定并行,可能交替执行),其他线程也在执行

垃圾收集器
新生代: Serial;ParNew;Parallel Scavenge
老年代:CMS;Serial Old; Parallel Old
皆可:G1

Serial收集器
单线程,GC时必须暂停所有线程
高效,简单,适用于单CPU环境

ParNew收集器
多线程版Serial,目前只有ParNew可以与CMS合用(多线程,单线程用Serial)

Parallel Scavenge收集器
使用复制算法,并行多线程,控制吞吐量(代码时间/(代码时间+GC时间))

Serial Old收集器
单线程,标记—整理算法,Server模式下,可以在JDK1.5前与Parallel Scavenge搭配,或作为CMS的后备预案

Parallel Old收集器
多线程,标记—整理算法,出现Parallel Old后,Parallel Scavenge可以放弃Serial Old,选择Parallel Old,在注重吞吐量的情况下可以使用

CMS收集器
以回收停顿时间最短为目标,基于标记—清除算法,分四步
1.初始标记
需要暂停所有线程,标记一下GC Root能关联到的对象,速度快
2.并发标记
GC Root Tracing过程(判断是否与GC Root相连)
3.重新标记
修正并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录(因为并发标记时是并发的)
时间较长,但比并发标记短
4.并发清除
清除对象
重新标记为并行,其他三个为并发
优点:并发收集,低停顿
缺点:对CPU资源敏感;无法处理浮动垃圾;碎片空间过多(标记—清除),调节会导致时间过长

G1收集器
面向服务器
G1将整个Java堆分为多个Region,跟踪各个Region里的垃圾堆积值大小,在后台维护一个优先列表,优先回收价值最大的Region。各个Region之间通过维系各自的Remembered Set进行查找,Set上记录的是谁引用了我。
大致步骤(不算Set):
初始标记:标记GC Root能直接关联到的对象
并发标记:可达性分析,时间较长
最终标记:修正并发标记期间变动的标记
筛选回收:对Region价值排序,优先回收
初始标记,并发标记为并发执行
最终标记,筛选回收为并行执行
优点:可以缩短停顿时间,更好的分代,碎片空间少

内存分配策略
1.大多数情况下,对象在新生代Eden区中分配,当没有足够空间时,JVM会发起一次 Minor GC
2.大对象(需要大量连续内存空间的Java对象例如字符串,数组)直接进入老年代
3.虚拟机定义了一个年龄计数器,如果对象在Eden出生,Minor GC后还存在,并能被Survivor容纳,就会移动到Survivor中,年龄设为1;之后每通过一次Minor GC,年龄加一,到达15岁(默认)会晋升到老年代
4.动态年龄判定:如果在Survivor空间中年龄相同的对象大于了一半,那么年龄大于等于该年龄的就可以进入老年代
5.空间分配担保:Minor GC前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有空间总对象,如果大于,Minor GC可以安全进行;如果不成立,会查看设置值是否允许担保失败,允许会再检查老年代最大可用连续空间是否大于晋升到老年代对象的平均大小,如果大于,尝试 MInor GC,否则改为进行Full GC

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值