Java垃圾回收
2019-08-05 10:53:25 1 举报
AI智能生成
Java垃圾回收机制的学习笔记
作者其他创作
大纲/内容
判断对象是否存活
引用计数
对象中添加一个引用计数器,别的地方引用一次,计数器+1。
引用失效时,计数器-1,为0时对象不可用。难以解决相互引用问题
引用失效时,计数器-1,为0时对象不可用。难以解决相互引用问题
可达性分析
对象到GC Roots没有任何引用链,则此对象不可用
常见的GC Roots(两栈两法)栈:执行上下文,法:全局性的引用
虚拟机栈(栈帧中的本地变量表)中引用的对象
本地方法栈JNI引用的对象
方法区中static属性引用的对象
方法区中常量引用的对象
4种引用
since jdk1.2
为什么要分这么多种类型?
因为1.2之前,引用的定义太纯粹:如果reference类型中存储的值代表另一块内存的起始地址,就称之为引用。
这样定义不灵活,对象要么就是被引用,要么没有。无法描述一些食之无味,弃之可惜的鸡肋对象。
为什么要分这么多种类型?
因为1.2之前,引用的定义太纯粹:如果reference类型中存储的值代表另一块内存的起始地址,就称之为引用。
这样定义不灵活,对象要么就是被引用,要么没有。无法描述一些食之无味,弃之可惜的鸡肋对象。
强引用(Strong Reference)
类似于 Object obj = new Object()这样的引用
只要存在,所引用的对象就绝不会被回收
软引用(Soft Reference)
用来描述有用非必需的对象
在OOM即将发生之前,将这些对象列进回收范围之中,如果回收之后还是不够,就抛OOM异常
弱引用(Weak Reference)
类似于软引用,但强度更弱
不管内存够不够,GC都会回收只被弱引用引用的对象
虚引用(Phantom Reference)
没什么用,有没有虚引用,对对象的生存时间没影响
唯一的目的就是:当对象被回收时收到一个系统通知,详见https://www.wikiwand.com/en/Phantom_reference
与GC Roots不可达的对象就非死不可吗?
答案是否,不可达了,还是有避免被宣判死刑的机会
具体的流程
被判定为不可达了
有必要执行finalize()方法吗?
即:1.对象有没有覆盖finalize()方法
2.finalize()方法是否已经被执行过了
即:1.对象有没有覆盖finalize()方法
2.finalize()方法是否已经被执行过了
覆盖了且没执行过
将对象放置在F-Queue队列中,稍后将被执行死刑。
自救办法:重新与引用链上的对象建立关系即可,如将this赋给类变量或者对象的成员变量
自救办法:重新与引用链上的对象建立关系即可,如将this赋给类变量或者对象的成员变量
没必要,不执行
方法区(HotSpot中的永久代)的垃圾回收
虚拟机规范中没有强制要求方法区实现GC
回收效率也确实不怎么高
但还是有些东西可以回收的
废弃常量:字面量,类、方法、字段的符号引用
比如常量池中有一个abc字符串常量,但没有被任何String对象引用
无用的类,满足以下3条件的类
该类的所有实例都已经被回收
加载该类的ClassLoader已经被回收
该类的java.lang.Class对象没有被引用,无法通过反射访问该类的方法
堆内存的回收,重点!!
算法介绍
标记清除
标记出需要回收的对象,然后回收
效率不高
回收过后,容易产生碎片。万一后续有个大对象需要内存,没有足够的连续内存,就不得不再来一次GC
复制算法
目的:为了解决第1种算法的效率问题。
思路:将内存分为大小一样的两块,每次只用一块。
一块用完了,就将还活着的对象复制到另一块,然后再清理。循环往复
思路:将内存分为大小一样的两块,每次只用一块。
一块用完了,就将还活着的对象复制到另一块,然后再清理。循环往复
缺点1:浪费空间
新生代的收集采用改良的复制算法
Eden:Survivor:Survivor = 8:1:1
缺点2:对象存活率高时:复制操作频繁,效率变低
标记整理
目的:为了解决第2种算法的缺点2
思路:标记出需要回收的对象,让存活的对象都往一端移动
然后清理掉端边界外的内存
适用于老年代
思路:标记出需要回收的对象,让存活的对象都往一端移动
然后清理掉端边界外的内存
适用于老年代
分代收集
思路:整合以上算法的优点,扬长避短,将内存分为新生代和老年代,各自使用不同的算法收集。
新生代的对象朝生夕灭,只有少量存活,那就用复制算法,付出一点点复制成本即可
老年代对象存活率高,并且不像新生代,新生代中哪怕内存不够存活对象用了,还有老年代来担保。
但老年代没得其他地方给他担保,所以就得采用标记清除或者标记整理算法
新生代的对象朝生夕灭,只有少量存活,那就用复制算法,付出一点点复制成本即可
老年代对象存活率高,并且不像新生代,新生代中哪怕内存不够存活对象用了,还有老年代来担保。
但老年代没得其他地方给他担保,所以就得采用标记清除或者标记整理算法
HotSpot的算法实现
1.可达性分析,存在的难点:
从GC Roots 节点找引用太花时间
GC停顿很敏感,即:不能出现分析期间对象的引用关系还在变化的情况
就好像,你妈在打扫房间,你还在丢垃圾,这样准确性无法得到保证。
这也是为什么存在STW的原因,因为要保证一致性,我分析的时候,你们都别丢垃圾了。
就好像,你妈在打扫房间,你还在丢垃圾,这样准确性无法得到保证。
这也是为什么存在STW的原因,因为要保证一致性,我分析的时候,你们都别丢垃圾了。
2.解决的办法
OopMap
在OopMap的帮助下,HotSpot可以很快找到GC Roots
3.安全点
不是什么时候都能开始GC的,必须要到到达安全点。
安全点既不能过少,以导致GC等待时间太长
也不能过多,以导致太多导致GC个不停
安全点既不能过少,以导致GC等待时间太长
也不能过多,以导致太多导致GC个不停
4.安全区域
直接内存
堆外内存
程序计数器、虚拟机栈、本地方法栈是线程私有的。
编译期就已经确定了其内存大小,方法或者线程一结束,内存自然被回收。
所以不存在垃圾回收!!!所以垃圾回收只针对堆内存和方法区
编译期就已经确定了其内存大小,方法或者线程一结束,内存自然被回收。
所以不存在垃圾回收!!!所以垃圾回收只针对堆内存和方法区
0 条评论
下一页