我是靠谱客的博主 野性眼神,最近开发中收集的这篇文章主要介绍【JVM】垃圾回收算法、堆内存分配与GC过程一、JVM垃圾回收区域二、垃圾回收算法三、堆内存分配与GC过程,觉得挺不错的,现在分享给大家,希望可以做个参考。
概述
目录
- 一、JVM垃圾回收区域
- 1.方法区
- 1. 常量池中废弃的常量
- 2. 不再使用的类
- 2. 堆垃圾回收
- 1. 判断堆中对象是否可被回收
- 1. 引用计数法
- 2. 可达性分析法
- 引用的类型
- 二、垃圾回收算法
- 1. 标记-清除
- 2. 标记-整理
- 3. 复制
- 4. 分代收集
- 三、堆内存分配与GC过程
- 堆内存-复制算法
- 堆内存-分配策略
- 堆内存-GC类型
- Minor GC
- Full GC
- 方法区、永久代、元空间之间的关系
一、JVM垃圾回收区域
垃圾回收针对于堆和方法区,对于程序计数器、栈,属于线程私有,只存在于线程的生命周期内,线程结束之后也会自动消失,因此不需要对此进行垃圾回收。
1.方法区
方法区的垃圾收集主要回收两部分内容:常量池中废弃的常量和不再使用的类型。
1. 常量池中废弃的常量
- 只要没有被任何地方引用,就可以被回收
2. 不再使用的类
- 该类所有的实例都已经被回收,也就是 Java 堆中不存在该类及其任何派生子类的实例
- 加载该类的类加载器已经被回收
- 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法
2. 堆垃圾回收
1. 判断堆中对象是否可被回收
无用的堆对象,计算方式:
1. 引用计数法
- 给对象添加一个引用计数器,当被引用一次就+1,引用失效一次-1,当计数为0时代表此对象没有被引用,可被回收。
- 但是这种计算方式,处理不了两个对象的循环引用
2. 可达性分析法
- 通过GC Roots作为起始点,不可达到的对象将被回收
- GC Roots包含的内容:
- 栈中引用的对象
- 方法区中静态属性引用的对象
- 方法区中常量引用的对象
引用的类型
- 强引用strong reference
new Object() 只要强引用还在,GC永远不会回收 - 软引用soft reference
在内存不够时被回收 - 弱引用weak reference
对象只能生存到下一次GC之前 - 虚引用phantom reference
为一个对象设置虚引用关联的唯一目的就是能在这个对象被回收时收到一个系统通知
二、垃圾回收算法
1. 标记-清除
- 将存活的对象进行标记,然后清理掉未被标记的对象。
- 不足:
- 标记和清除过程效率都不高;
- 会产生大量不连续的 内存碎片 ,导致无法给大对象分配内存
2. 标记-整理
- 让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存
- 标记-整理是在标记-清除的基础上,对剩余对象进行移动整理,虽然解决了内存碎片问题,但是成本更高。
3. 复制
- 将内存划分为大小相等的两块,每次只使用其中一块,当这一块内存用完了就将还存活的对象复制到另一块上面,然后再把使用过的内存空间进行一次清理。
- 不足:只使用了内存的一半
4. 分代收集
- 根据对象存活周期将内存划分为几块,不同块采用适当的收集算法。
一般将堆分为新生代和老年代。 - 新生代使用: 复制算法
- 老年代使用: 标记-清除、或标记-整理
三、堆内存分配与GC过程
以对象存活的周期来划分,堆内存可分为:
- 新生代
- Eden
- Survivor From 幸存者空间
- Survivor To 幸存者空间
- 老年代
- 元空间(永久代):像一些方法中的操作临时对象等,JDK1.8 之前是占用 JVM 内存,JDK1.8 之后直接使用物理内存
- HotSpot JVM把年轻代分为了三部分:1个Eden区和2个Survivor区(分别叫from和to)默认比例为8:1。
这个比例也是为了解决复制算法中,内存只能使用一半的缺陷。因为堆内存中的对象每次GC只有少数可以活下来,没有必要等分新生代。 - 在堆内存新生代中,使用的是复制算法
堆内存-复制算法
详细过程:
- 一般情况下,新创建的对象都会被分配到Eden区(一些大对象特殊处理)
在GC开始的时候,对象只会存在于Eden区和名为From的Survivor区,Survivor区To是空的 - GC开始
- 将Eden存活对象,复制到To区
- 而From区存活的对象,根据其年龄值决定去处:超出阈值直接移动到老年代、否则复制到To区
(对象在Survivor区中每熬过一次Minor GC,年龄就会增加1岁,默认阈值为15次)
- 将Eden区和From区清空,这时一次GC已完成
下一次From和To会交换他们的角色
堆内存-分配策略
- 对象优先在新生代Eden中分配,当新生代没有内存时,将发起一次Minor gc
- 大对象直接进入老年代,大对象指,需要大量连续内存空间的java对象
- 长期存活的对象将进入老年代,survivor中的对象每熬过一次minor gc,age就增加一岁(默认15岁进入老年代)
- 空间分配担保:
无法保证每次回收存活的对象都在To区中,即存活对象小于10%,Survivor无法容纳的对象将直接进入老年代区。
在这之前,虚拟机先检查老年代最大可用的连续空间是否大于新生代所有对象总空间:
如果条件成立的话,那么 Minor GC 可以确认是安全的。- 如果不成立的话虚拟机会查看 HandlePromotionFailure 设置值是否允许担保失败。如果允许那么就会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次 Minor GC;
- 如果小于,或者 HandlePromotionFailure 设置不允许冒险,那么就要进行一次 Full GC
堆内存-GC类型
Minor GC
- 只回收新生代
因为新生代对象存活时间很短,因此 Minor GC 会频繁执行,执行的速度一般也会比较快。 - 触发条件:
当Eden区满时,将会触发一次MinorGC
Full GC
指整堆回收,包括年轻代Young、老年代Old、永久代Perm。
- MajorGC:只回收老年代
老年代对象其存活时间长,因此 Full GC 很少执行,执行速度会比 Minor GC 慢很多 - 触发条件:
- 调用System.gc()
- 老年代空间不足
- 空间分配担保失败:每次担保失败,就执行一次FullGC
- JDK1.7之前的方法区(永久代)空间不足(当系统中要加载的类、反射的类和调用的方法较多时,永久代可能会被占满)
方法区、永久代、元空间之间的关系
- Q:永久代与方法区的关系?
A:可以认为是永久代实现了方法区。永久代是Hotspot中的一个概念,其他的jvm中也许并没有这个说法。HotSpot 可以使用如下参数来调节方法区的大小:- -XX:PermSize——方法区初始大小
- -XX:MaxPermSize——方法区最大大小
超过这个值将会抛出OutOfMemoryError异常:java.lang.OutOfMemoryError: PermGen
- Q:元空间MetaSpace与永久代Perm的区别?
A:对于Java8, HotSpots取消了永久代,取代永久代的就是元空间。- 存储位置不同:永久代物理是是堆的一部分,和新生代,老年代地址是连续的
而元空间属于本地内存; - 存储内容不同:元空间存储类的元信息,静态变量和常量池等并入堆中
相当于永久代的数据被分到了堆和元空间中
- 存储位置不同:永久代物理是是堆的一部分,和新生代,老年代地址是连续的
最后
以上就是野性眼神为你收集整理的【JVM】垃圾回收算法、堆内存分配与GC过程一、JVM垃圾回收区域二、垃圾回收算法三、堆内存分配与GC过程的全部内容,希望文章能够帮你解决【JVM】垃圾回收算法、堆内存分配与GC过程一、JVM垃圾回收区域二、垃圾回收算法三、堆内存分配与GC过程所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复