什么是GC
Garbage Collection,简称GC,本质上就是内存管理回收技术(主要指的是heap),在具体探究问题前,我们需要带着一些问题来亚就这个机制:
- 哪些内存需要回收
- 什么时候回收
- 如何回收
为什么GC不需要对内存栈(stack)等其他内存区域进行管理?
这里主要是因为程序技术其、虚拟机栈以及本地方法栈的都是依附于进程存在,且分配内存的大小都是已知的,在编译过程中都已经确定好了进栈出栈的顺序。而对于堆来说,对象的内存大小是不确定的,在编译过程是不可知的,而只有在运行时才知道。
如何判断对象的有效性
引用计数法
对一个对象的引用次数进行计数,当引用次数为0时,即相当于对象已经失效,go dead了。但是jvm并没有采用这种方式。
缺点:对于引用环无法处理,a引用b,b引用a,则引用计数永不是0。
可达性分析
对于任何活着的对象,一定能追溯到其存活在堆栈或静态存储区之中的引用。从GC Root开始一直到其引用的对象,如果其间没有任何可用的引用链可达,则说明这个对象时可以回收的。
在java中可以作为GC roots包括:
- 虚拟机栈中的本地变量表的引用对象。
- 方法区内的静态属性引用对象。
- 方法区内的常量引用的对象。
- 本地方法中的JNI应用对象
垃圾收集算法
标记清除法
两阶段的清除算法:1)首先标记出需要回收的对象(可达性分析);2)标记完成后,进行一次性清除;
缺点:产生大量的不连续的内存碎片,另外就是效率比较低。
复制算法(针对对象存活率低)
新生代内存空间中对象的特点就是存活时间短。
将内存分为两块,每次使用其中的一块,将存活的对象复制到另一个部分,简单高效,缺点就是费内存。
对于jvm新生代的内存,就是采用此种方法,但是其比例则默认为8:1,首先来说新生代内存主要分为两个部分,一个eden与两个survivor空间,将其中eden和另一个survivor中的活着的对象复制到另一块survivor上,此外还有10%空间的富裕。当新生代不足的时候就会通过分配机制复制到老生代。
标记整理算法(针对对象存活率高)
复制算法在对象存活率较高的时候,效率很低,于是就标记清除变为了标记整理,首先将存活的对象向一个方向移动,将边界外的内存都清除掉。
分代收集算法
本质上就是因地制宜算法,将堆内存分成老生代和新生代,按照其特点使用合适的算法,对于新生代采用复制算法,对于老生代采用标记清除或者标记整理。
理解GC日志
[gc][young][93647][8255] duration [423ms], collections [1]/[1s], total [423ms]/[29.1m], memory [10.5gb]->[11gb]/[15.8gb],
all_pools {[young] [1.1gb]->[1.2gb]/[1.4gb]}{[survivor] [191.3mb]->[191.3mb]/[191.3mb]}{[old] [9.2gb]->[9.6gb]/[14.1gb]}
- young 新生代
- old老生代
- [1.1gb]->[1.2gb]/[1.4gb]:使用内存->GC后内存/总内存
- total [423ms]/[29.1m]:本地GC时间/总时间