深入理解Java虚拟机-第三章GC与内存分配

第三章GC与内存分配

3.1概述

程序计数器、虚拟机栈、本地方法栈3个区域随线程生灭,栈中的栈帧分配多少内存是确定的,因此这几个区域的内存分配具有确定性,内存会随着方法、线程的结束自动回收。
堆和方法区则不同,一个接口中的多个实现类需要的内存可能不一样,一个方法中的多个分支需要的内存可能也不一样,只有程序运行时才知道会创建哪些对象,内存的分配和回收都是动态的。

3.2 如何确定对象存活、死去

3.2.1 引用计数算法

给对象添加一个引用计数器,有一个地方引用他+1,引用失效是-1,任何时刻计数器为0时就不可能再被使用。
缺陷:很难解决对象直接相互循环引用的问题

3.2.2 可达性分析算法

基本思路:从一系列的GC Root对象作为起点,从这些节点开始向下搜素,搜索所走过的路径称为引用链,当一个对象没有任何引用链相连,则对象不可用。
java中可作为gc root的对象
1.虚拟机栈(栈帧中的本地变量表)中引用的对象
2.方法去中类静态属性引用的对象
3.方法区中常量引用的对象
4.本地方法栈中native方法引用的对象

3.2.3 引用

java1.2以前引用的定义:如果reference类型的数据中存储的数值代表的另外一块内存的起始地址,就称这块内存代表着一个引用。
1.2以后扩充为强引用、软引用、弱引用、虚引用;
强引用:只要存在就不会被回收
软引用:在发生内存溢出异常之前,对其进行回收
弱引用:只能活到下次gc
虚引用:不会对生存时间发生影响,设置的目的是在对象被回收时,收到一个系统通知

3.2.4 生存还是死亡

即使是不可达的对象,也并非是非死不可,真正宣告一个对象死亡需要至少两次标记。
第一次标记:将可达性分析时不 可达的对象进行标记,并进行一次筛选(是否有必要进行finalize()方法),将需要执行finalize方法的对象放入F-Queue中。
第二次标记:F-Queue中没有重新关联的对象被二次标记。

3.2.5 回收方法区(Hotspot的永生代)

永生代主要回收两部分内容:废弃常量和无用的类
判断废弃常量和堆中类似。
判断无用的类比较复杂:
1.该类的所有的实例都已经被回收
2.加载该类的classloader已经被回收
3.该类对应的java.lang.class对象没有在任何地方被引用,无法在任何地方通过反射访问该类方法

3.3垃圾回收算法

3.3.1标记-清除算法

分为两个阶段:标记和清除
标记过程为上面的对象标记判定。
有两个不足:
1.效率问题,标记和清除的的小路都不高;
2.空间问题,标记清楚后会产生大量不连续的内存碎片

3.3.2复制算法

内存一分为二,一次只用一半,实现简单,但是浪费太大

3.3.3标记-整理算法

标记过程与标记-清除算法一样,后续步骤不是直接对可回收对象进行清理,而是让所有存活对象移动到一端,然后直接清理边界之外的内存。

3.3.4分代收集算法

根据对象存活周期的不同,将java堆分成新生代和老年代。
新生代使用复制算法
老年代使用标记-清理或标记-整理算法

3.4HotSpot的算法实现

3.4.1枚举根节点

从GCRoot节点找引用链为例,可作为GC Root的节点主要在全局性的引用与执行上下文中,如果逐个查找必定浪费时间。
GC停顿,分析工作必须在一个能确保一致性的快照中执行,一致性是指此时系统看起来像是冻结在某个时间点上,不能出现分析过程中对象引用关系还在不断的变化的情况。
在停顿时,虚拟机需要知道哪些地方存放对象的引用,在HotSpot中,使用一组称为OopMap的数据结构来达到这个目的,在类加载完成的时候,HotSpot就把对象内的偏移量计算出来。

3.4.2安全点

HotSpot没有为每条指令都生成OopMap,只是在特定的位置记录了这些信息,这些位置称为安全点。
对于安全点,如何在GC发生时让所有线程都跑到安全点上再停顿。
1.抢占式中断
在GC发生时,把所有线程都中断,如果线程不在安全点,就恢复线程,让其运行到安全点。
2.主动式中断
当GC需要中断线程的时候,不直接对线程操作,仅仅简单地设置一个标志,各个线程执行时主动轮询这个标志,发现中断标志为真时,就自己中断挂起。

3.4.3安全区域

安全域是指在一段代码片段中,引用关系不会发生变化,在这个区域中的任意地方开始GC都是安全的。

3.5垃圾收集器

G1收集器

特点:①并行与并发②分代收集③空间集合④可预测的停顿
使用G1收集器时,java堆的内存布局就与其他收集器有很大区别,他将整个java堆划分成多个相等的独立区域,虽然保留新生代老年代概念,但不物理隔离,都是region的一部分。
G1跟踪各个region里面的垃圾堆积的价值大小,在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的region
G1收集器中,region之间的对象引用以及其他收集器中的新生代和老年代之间的对象引用,虚拟机都是用remembered set来避免全堆扫描的。G1中每个region都会有一个与之对应的remembered set,当虚拟机发现程序对reference类型的对象进行写操作时,会产生一个write barrier暂时中断写操作,检查reference引用的对象是否在不同的region之中,如果是分代就是检查老年代中的对象是否引用了新生代中的对象,如果引用了,就通过cardtable把相关引用信息记录到被引用对象所属的region的remembered set中。这样就保证gc回收时,不需要扫描全堆就可以不留遗漏。
G1运作步骤:
1.初始标记
2.并发标记
3.最终标记
4.筛选回收

3.6内存分配与回收策略

3.6.1对象优先在eden分配

3.6.2大对象直接进入老年代

3.6.3长期存活的对象进入老年代

3.6.4动态对象年龄判断

如果在survivor空间中相同年龄所有对象大小的总和大于survivor空间的一半,年龄大于等于该年龄的对象就直接进入老年代

3.6.5空间分配担保

在发生MinorGC前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果成立,那么minorGC可以确保是安全的。如果不成立,查看HandlePromotionFailure设置值是否允许担保失败,如果允许,就继续检查老年代最大可用的连续空间是否大于历次晋升到老年代的平均大小,如果大于,尝试一次minorGC,否则就fullGC

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值