1.如何判断对象可以回收
1.1.引用计数法
定义:给对象添加一个引用计数器,每当有一个地方引用它,计数器值就加一;相反的,当引用失效的时候,计数器值就减一;任何时刻计数器为0的对象就是不可能再被使用的。
弊端:可能存在无效的循环引用
现在主流的Java虚拟机里并没有选用引用计数算法来管理内存,最主要的原因就是它很难解决对象之间相互循环引用的问题
1.2.可达性分析算法
- Java虚拟机中的垃圾回收器采用可达性分析来探索所有存活的对象
- 扫描堆中的对象,看是否能够沿着GC Root对象为起点的引用链找到该对象,找不到,表示可以回收
- 哪些对象可以作为GC Root?
- 虚拟机栈(栈帧中的本地变量表)中引用的对象。
- 方法区中类静态属性引用的对象。
- 方法区中常量引用的对象。
- 本地方法栈中JNI(即一般说的Native方法)引用的对象。
1.3.四种引用
- 强引用
- 只有所有GX Roots对象都不通过【强引用】引用该对象,该对象才能被垃圾回收
- 软引用(SoftReference)
- 仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次触发垃圾回收,回收软引用对象
- 可以配合引用队列来释放软引用自身
- 弱引用(WeakReference)
- 仅有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象
- 可以配合引用队列来释放弱引用自身
- 虚引用(PhantomReference)
- 必须配合引用队列使用,主要配合ByteBuffer使用,被引用对象回收时,会将虚引用入队,由Reference Handler线程调用虚引用相关方法释放直接内存
- 终结器引用(FinalReference)
- 无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用入队(被引用对象暂时没有被回收),再由Finalizer线程通过终结器引用找到被引用对象并调用它的finalize方法,第二次GC时才能回收被引用对象
2.垃圾回收算法
2.1.标记清除
定义:Mark Sweep
- 速度较快
- 会造成内存碎片
2.2.标记整理
定义:Mark Compact
- 速度慢
- 没有内存碎片
2.3.复制
定义:Copy
- 不会有内存碎片
- 需要占用双倍内存空间
3.分代垃圾回收
- 对象首先分配在伊甸园区域
- 新生代空间不足时,触发minor gc,伊甸园和from存活的对象使用copy复制到to中,存活的对象年龄加1并且交换from to
- minor gc会引发stop the world,暂停其它用户的线程,等来及回收结束后,用户线程才恢复运行
- 当对象寿命超过阈值,会晋升至老年代,最大寿命是15(4bit)
- 当老年代空间不足,会先尝试触发minor gc,如果之后空间仍不足,那么触发full gc,STW的时间更长
3.1相关JVM参数
4.垃圾回收器
- 串行
- 单线程
- 堆内存较小,适合个人电脑
- 吞吐量优先
- 多线程
- 堆内存较大,多核cpu
- 让单位时间内STW的时间最短
- 响应时间优先
- 多线程
- 堆内存较大,多核cpu
- 尽可能让单次STW的时间最短
4.1.串行
4.2.吞吐量优先
4.3.响应时间优先
4.4.G1
定义:Garbage First
适用场景
- 同时注重吞吐量(Throughput)和低延迟(Low latency),默认的暂停目标是200ms
- 超大堆内存,会将堆划分为多个大小相等的Region
- 整体上是标记+整理算法,两个区域之间是复制算法
相关JVM参数
-XX:+UseG1GC
-XX:G1HeapRegionSize=size
-XX:MaxGCPauseMillis=time
-
G1垃圾回收阶段
-
Young Collection
- 会STW
- Young Collection + CM
- 在Young GC时会进行GC Root的初始标记
- 老年代占用堆空间比例达到阈值时,进行并发标记(不会STW),由下面的参数决定
-XX:InitiatingHeapOccupancyPercent=percent(默认45%)
- Mixed Collection
会对E、S、O进行全面垃圾回收
- 最终标记(Remark)会STW
- 拷贝存活(Evacuation)会STW
-XX:MaxGCPauseMillis=ms
- Full GC
- SerialGC
- 新生代内存不足发生的垃圾收集-minor gc
- 老年代内存不足发生的垃圾收集-full gc
- ParallelGC
- 新生代内存不足发生的垃圾收集-minor gc
- 老年代内存不足发生的垃圾收集-full gc
- CMS
- 新生代内存不足发生的垃圾收集-minor gc
- 老年代内存不足
- G1
- 新生代内存不足发生的垃圾收集-minor gc
- 老年代内存不足(垃圾回收速度低于产生速度时,并发失败,退化为单线程SerialGC串行执行)
- Young Collection跨代引用
-
新生代回收的跨代引用(老年代引用新生代)问题
-
卡表与Remembered Set
-
在引用变更时通过post -write barrier + dirty card queue
-
concurrent refinement threads更新Remembered Set
- Remark
- pre-write barrier + satb_mark_queue
- JDK 8u20字符串去重
- 优点:节省大量内存
- 缺点:略微多占用了cpu时间,新生代回收时间略微增加
-XX:+UseStringDeduplication
- 将所有新分配的字符串放入一个队列
- 当新生代回收时,G1并发检查是否有字符串去重
- 如果它们的值一样,让它们医用同一个char[]
- 注意,与String.intern()不一样
- String.intern()关注的是字符串对象
- 而字符串去重关注的是char[]
- 在JVM内部,使用了不同的字符串表