背景
与C++对比,C++的内存回收是有C++的代码控制的,而JAVA的内存回收是由JVM的垃圾回收器控制的,看起来JAVA的垃圾回收更“自动化”,但是当需要排查内存溢出和内存泄漏问题时,垃圾回收器成为系统的瓶颈,此时就需要对这些“自动化”的技术实施监控和调节。
- 内存溢出:程序报警分配的内存空间不够用
- 内存泄漏:是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。
- 内存泄漏会导致内存溢出
GC的区域
jvm的运行时数据区主要分为方法区、堆、虚拟机栈、本地方法栈、程序计数器。
其中,虚拟机栈、本地方法栈、程序计数器是线程私有的,一旦线程执行完成,内存就会自然回收。
方法区、堆是线程共用的,不会随着线程结束而回收,所以GC主要是针对方法区和堆。
GC的的目标对象
决定对象的内存空间是否需要被回收,主要由两种方式引用计数和可达性分析
引用计数
引用计数的方式可以简单高效的实现,但是因为存在缺陷(一旦出现循环引用则永远不能被回收),所以没有的jvm的GC中是用,
可达性分析
可达性分析是指从GC ROOTS出发,根据依赖关系是否可以达到指定的对象,如果可达,则对象不能被回收,如果不可能,则对象可以被回收。
另外,除了强引用外,还有软引用、弱引用、虚引用等,主要一次随着应用强度而减弱,而在空间回收时,按照相反次序进行内存回收。
GC roots的对象主要扩一下四种:
- 虚拟机栈引用的对象
- 本地方法栈应用的对象
- 方法区引用的对象
- 常量池引用的对象
GC的算法
垃圾回收的算法主要包括四种:
- 标记-清除
- 标记-复制
- 标记-整理
- 分带收集算法
标记-清除
一次标记,一次请求,缺点是回收效率低,并且造成大量的内存碎片,虽然有诸多缺点但是却是其他后续的算法的基础
标记-复制
将空间分成相等的两部分,每次只使用一块,虽然效率提高,但是也造成了大量的空间浪费
标记-整理
在“标记-清除”的基础上,对剩余的空间进行碎片整理,这样提高了空间的使用率,降低了下次分配内存空间因为连续空间不足而导致需要再次GC的问题
分带收集算法
根据对象的生命周期特性,90%以上新生对象是很快就要被回收的,所以整体上将内存空间分为新生代和老年代,并且将新生代分为Eden和两个相等的Survivor区。新生对象分配的空间在Eden和一个survivor中,每次minor GC后,将存货的对象负责到另一个survivor上。
默认参数-XX:SurvivorRatio=8
Eden:s0:s1=8:1:1
GC的类型
- serial
- ParNew
- CMS
- Parallel
- parallel Old
- serial old
- G1
GC的日志
- 进程实例运行时间
- GC类型
- GC区域
- 区域的内存空间变化
- 实例的内存空间变化
网友评论