摘录自《深入理解Java虚拟机》 第二版 周志明
- 以下内容都是我对书籍内容的个人理解,可能存在错误,若要深入学习,建议翻看原书籍
简述
GC,即Garbage Collection,垃圾回收机制,协助程序员自动管理内存,通常作为一个单独的低优先级的线程运行,因为内存空间有限,需要将无用的资源进行回收,防止内存泄漏
四种引用状态:
| 类型 | 描述 | Java类 |
|---|---|---|
| 强引用 | 只要强引用还存在,GC宁可抛出OutOfMemoryError也不会回收对象 | Object obj=new Object()就是强引用 |
| 软引用 | 有用但非必须的对象,在系统将要发生内存溢出异常之前,会将这些对象列进回收范围进行二次回收 | SoftReference |
| 弱引用 | 非必需对象,只能存活到下一次垃圾回收之前,GC开始回收内存时,无论内存是否足够,弱引用对象都会被回收 | WeakReference |
| 虚引用 | 设置虚引用的唯一目的就是希望垃圾收集时收到系统通知 | PhantomReference |
垃圾分析算法
| 算法 | 描述 | 运用 |
|---|---|---|
| 引用计数算法 | 每当有一个地方引用该对象,则计数器+1,当引用失效时,计数器-1,当计数器为0时,便意味着该对象不再被引用,此时GC将对该对象进行回收 | Redis(难以解决对象之间相互引用的问题,所以Java并没有用) |
| 可达性分析法 | 通过一系列”GC Roots”的对象为起始点,从这些节点向下搜索,搜索所经过的路径称为引用链,当一个对象无法到达GC Root时,此时GC将回收该对象占用的内存 | Java |
GCRoots对象:
- 虚拟机栈(栈帧中局部变量表)中引用的对象
- 方法区中类静态变量引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI引用的对象
垃圾收集算法
- 标记-清除算法(Mark-Sweep):分为“标记”和“清除”两个阶段,首先标记出需要回收的对象,标记完成后完成统一回收。缺点明显,效率低,标记清除后产生大量不连续的内存空间,当程序运行时需要为一个较大的对象分配内存时,无法找到足够的连续空间,会不得不提前触发下一次垃圾收集动作
- 复制算法(Copying):将可用内存分为两块,每次只使用其中一块,当一块内存用完了,就将还存活着的对象复制到另一块内存上,然后再把原先的内存全部进行回收。缺点明显,内存缩小为原来的一半,如果原先内存存在大量存活对象,复制这部分对象到新内存中消耗大量时间
- 标记整理算法(Mark-Compact):让所有存活对象都向一端移动,然后直接清理掉边界以外的内存
-
分代收集算法(商用虚拟机):
A)新生代(采用复制算法):
① 新生代中的绝大多数对象生命周期非常短,存活率极低,所以很适合复制算法,但是50%-50%分太浪费,一般将新生代分为Eden(80%,Eden是连续的存储空间,因此分配内存极快)、Survivor(10%) from、Survivor to(10%)三部分,其中Survivor总有一个区域(10%)是空白的,Survivor另一区域存放从上一次Minor GC中幸存的对象。当Minor GC触发,仍然存活的对象被复制到空白区域,然后清空这两部分(90%)的内存;
②当对象在Survivor区躲过一次GC后,年龄+1,默认当年龄满15时,对象便会被转移到年老代中
③如果空白空间无法存放仍然存活的对象,此时使用内存分配担保机制,将新生代存活的对象复制到年老代,同时对于创建大对象时,如果新生代无足够空间,也直接在年老代中分配内存;
B) 年老代(采用标记-整理算法):
①年老代的对象一般生命周期较长,存活率较高,所以适合标记-整理算法
②当新生代和年老代的内存空间不足,GC回收后仍然不足,且无法再扩展时,堆就会产生OutOfMemoryError异常
C) 永生代(采用标记-整理算法):
①Sun HotSpot虚拟机称java虚拟内存中的方法区为永生代,是被各个线程共享的内存区域方法区的垃圾回收
②方法区主要回收废弃常量(没有任何一个变量引用该常量)和无用的类(满足三个条件:Ⅰ该类所有的实例都已经被回收;Ⅱ加载该类的ClassLoader已经被回收;Ⅲ该类对应的Class对象没有在任何地方被引用)
- 垃圾回收分类
①Minor GC:只针对新生代的GC,次数频繁,持续时间短
②Major GC/Full GC:针对所有分代区域,次数少,持续时间长