16.GC:只能自动回收机制,手动提醒
16.1 GC两种分类
- 针对HotSpot VM的实现,它里面的GC按照回收区域又分为两大种类型:一种是部分收集(Partial GC),一种是整堆收集(Full GC)
- jvm在进行GC时,并不是对这3个区域进行统一回收。大部分时候回收的是新生代
- Eden:MinorGC,经过GC后Eden空闲,存活对象移入幸存区
- 幸存区(0、1)from/to
- 老年区 Major GC
- GC三种类:轻GC(普通)MinorGC–针对新生代和偶尔的幸存区
- Major GC针对老年区
- 重GC(全局GC)FullGC–整堆回收
- Full GC 是开发或调优中尽量要避免的。这样暂停时间会短一些。
16.2 各GC触发机制
A.年轻代GC(Minor GC)触发机制:
a. 当年轻代空间不足时,就会触发Minor GC,这里的年轻代满指的是Eden代满,Survivor满不会引发GC.(每次 Minor GC会清理年轻代的内存。)
b. 因为Java对象大多都是具备短期内存活的特性,所以 Minor GC 非常频繁,一般回收速度也比较快。这一定义即清晰又易于理解。
c.Minor GC会引发 STW(暂停其他用户的线程,等垃圾回收结束,用户线程才恢复运行)。
B.老年代GC(Major GC/Full GC)触发机制:
a.指发生在老年代的GC,对象从老年代消失时,我们说“Major GC” 或“ Full GC”发生了。
b.出现Major GC,经常会伴随至少一次的Minor GC(但非绝对的,在Parallel Scavenge 收集器的收集策略里就有直接进行Major GC的策略选择过程)。也就是在老年代空间不足时,会先触发Minor GC。如果之后空间不足,则触发Major GC。
c.Major GC 的速度一般会比Minor GC慢10倍以上,STW的时间更长。
d.如果Major GC后,内存还不足,就报OOM了。
C.Full GC触发机制:
a.调用 System.gc()时,系统建议执行Full GC,但是不必执行。
b.老年代不足/方法区空间不足。
d.通过Minor GC后进入老年代的对象大小大于老年代可用内存。
e.由Eden区、s0区向s1区赋值时,对象大于s0可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小。
16.3.常用算法
- 标记清除法(新生区)
- 标记整理(压缩)
- 复制算法:标记存活次数,复制移入空区域(新生区)
- 引用计数法:IOS,微软com技术、python语言等
- 可达性分析算法:java、C#、jvm使用
a.引用计数法:和可达性分析算法是判断对象状态的算法(存活)。
-
不高效,springBoot项目大量的对象都用计数器…
-
死循环问题
b.可达性分析算法:称为根搜索算法、追踪性垃圾收集 -
可达性分析算法不仅同样具备实现简单和执行高效等特点,更重要的是该算法可以有效地解决在引用计数算法中循环引用的问题,防止内存泄漏的发生
-
可达性分析算法是以根对象集合(GCRoots)为起始点,按照从上至下的方式搜索被根对象集合所连接的目标对象是否可达。
-
使用可达性分析算法后,内存中的存活对象都会被根对象集合直接或间接连接着,搜索所走过的路径称为引用链(Reference Chain)。
-
如果目标对象没有任何引用链相连,则是不可达的,就意味着该对象己经死亡,可以标记为垃圾对象。
-
在可达性分析算法中,只有能够被根对象集合直接或者间接连接的对象才是存活对象。
-
GC Roots可以是那些:
-
- 虚拟机栈中引用的对象:各个线程被调用的方法中使用到的参数、局部变量等。
- 本地方法栈内 JNI(通常说的本地方法)引用的对象
- 方法区中类静态属性引用的对象:Java类的引用类型静态变量
- 方法区中常量引用的对象:字符串常量池(string Table)里的引用
- 所有被同步锁 synchronized 持有的对象
- Java虚拟机内部的引用:基本数据类型对应的 Class 对象,一些常驻的异常对象(如:NullPointerException、OutOfMemoryError),系统类加载器
- 反映 java 虚拟机内部情况的 JMXBean、JVMTI 中注册的回调、本地代码缓存等
- 即堆空间外的一些结构,比如虚拟机栈、本地方法栈、方法区、字符串常量池等地方对堆空间进行引用的,都可以作为 GC Roots 进行可达性分析
- 注意:要使用可达性分析算法来判断内存是否可回收,那么分析工作必须在一个能保障一致性的快照中进行:导致 GC进行时必须“stop The World”的一个重要原因
c.复制算法:多空间,不浪费时间
-
对幸存0/1区,判断谁是to—原则:谁空谁是to
-
Jvm 启动的时候会初始化标记from和to,第一次minorGC就会清理eden 和from,把幸存对象放到to,并且把to标记为from
-
幸存区满了之后,将幸存1区即from区的幸存对象复制存入幸存2区即to区,清空1区,1区变成to区
-
每次minorGC后会将Edon和有存活对象的S区活的对象移到空的幸存区:一旦Eden区被GC后就会是空的。
-
解决了内存碎片化问题,内存碎片不连续导致明明内存足够缺无法NEW对象
-
自己设定一个临界值,存活的次数超过这个临界值就直接进去老年代
-
当一个对象经历了15次gc,都还没有死亡,进入老年区
-
好处:没有内存碎片
-
坏处:浪费内存空间(一个幸存区)。假设对象100%存活(极端情况)复制一次复杂度好了。
-
最佳使用场景:对象存活度较低的时候,新生区。
d.标记清除法 -
判断对象是否存活用可达性分析法
-
优点:不需额外的空间
-
缺点:两次扫码,严重浪费时间,会产生碎片。new 对象时进行hash计算地址
-
引用计数是引用开始+1,引用结束-1·,当为0时就立即清除了
-
判断对象的死活使用根搜索算法(可达性分析算法、追踪性垃圾收集)或者标记-清除算法,但一般不用后者
-
所有内存标记满了,会停止程序
d.标记整理(压缩):是对标记清理的在优化(空间优化) -
在标记清理算法两次扫描的基础上再进行一次扫描,向一端移动存活对象,存活对象之外的对象进行清理,防止了内存碎片的产生
-
缺点:多了一次移动时间成本
-
如果在优化:先标记清除几次,在进行压缩。减少了几次压缩时间
17.总结
- 内存效率/时间复杂度:复制算法>标记清除算法>标记压缩算法
- 内存整齐度:复制算法=标记压缩算法>标记清除算法
- 内存利用率/空间复杂度:标记清除算法=标记压缩算法>复制算法
- 没有最好算法,只有合适的算法:分代收集算法
- 新生代存活率低,复制算法是非常适合这个场景的
- 老年代老年代内存满后触发Full GC,使用标记(内存碎片不是太多)-整理算法进行
下一篇:jvm探究-03-jvm脉络梳理4-JMM协议
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/123970.html