Java-GC机制
背景:
- Java的内存管理实际上就是对象的管理,其中包括对象的分配和释放。对于程序员来说,分配对象使用new关键字;释放对象时,只要将对象所有引用赋值为null,让程序不能够再访问到这个对象,我们称该对象为"不可达的".GC将负责回收所有"不可达"对象的内存空间。
- 堆空间是用来存储new出来的对象的,当new出来的对象填充满时,会让堆爆掉,程序就挂了,这显然时不被允许的。
- Garbage Collection的机制就是判断堆内存中哪些对象能被删除,判断的标准就是GCRoot
GCRoot
- GCRoot:指向堆中的对象的引用,包含:
- 栈上的引用直接或者间接地被引用
- 方法区上全局的的Static变量&常量直接或者间接被引用
- 本地方法栈上的的C++直接或者间接被引用
- 那些可以直接存在的,没有和GCRoot有相连的关系的对象可以被删除
GC机制清理堆中垃圾的思路
-
**标记-清理:**在堆中要删除对象后面打标
- 缺点:标记和清除两次扫描会严重浪费时间,同时会产生内存碎片
- 优点:不需要额外空间
-
**标记-整理:**堆中删除的对象留下的空间能聚集在一起
- 缺点:代价太大,除了两次扫描还要把所有整理后的对象前移
- 优点:减少了内存碎片
-
**复制算法:**将整个内存一分为二,将1区需要删除的标记,然后将没打标记的对象紧凑复制到2区,既避免了内存碎片问题,整个内存开销又不是很大。
- 缺点:需要两倍内存空间
- 优点:不会产生碎片问题
- 最佳使用场景:对象的成活率较低(新生区就是这样)
-
实际的GC:将堆区划分
-
新new的对象都会在Eden(伊甸园)出生,当Eden快满的时候会触发young区域的GC,采用复制算法,将Eden中没有标记的对象复制到Survivor0(幸存0)区域
-
Eden很大,Survivor更小。因为对象都有一个特点:朝生夕死,很容易就夭折了,生死率大概是1:8
-
需要两块Survivor区交替工作(From和To的反复交替:谁空谁是To)
- E+S1 复制到 S0
- E+S0 复制到 S1
- E+S1 复制到 S0
- …(如此交替工作)
-
一个对象每幸存1次,其年龄就会+1,如果满了6岁,(满了6岁基本60岁都不会清理了)就不会复制到Survivor区了,就会直接到Old区了
-
大对象(例:1000万个元素的int数组)会直接存放到Old区。
-
Old区满了也会触放GC,OldGC一般也会伴随YoungGC,所以,Old区满了会触发FullGC,此时会触发stoptheworld,java程序全部暂停,报错OOM(java.lang.OutOfMemory :Java heap space),全力进行垃圾回收(采用标记清理 or 标记整理算法)
-
总结:
-
标记-清理和标记-整理 主要用在 FullGC机制
- 复制算法只要用在 YoungGC机制
-
内存效率(时间复杂度): 复制算法>标记清除>标记整理
- 内存整齐度: 复制算法=标记整理>标记清除
- 内存利用率: 标记整理=标记清除>复制算法
- 没有最好的算法,只有最合适的算法
-
垃圾收集器:
- 年轻代:ParNew:复制算法
- 老年代:CMS:标记-清理
- 最新版的JDK采用的是:G1垃圾收集器
-
-
永久区
该区域常驻内存,用来存放JDK自身携带的Class对象等。存储的是java运行时的一些环境。
该区域不存在垃圾回收,关闭虚拟机就会释放该区域的内存
- jdk1.6之前:永久代,常量池在方法区
- jdk1.7:永久代慢慢退化了,常量池在堆中
- jdk1.8之后:无永久代,整个方法区(包含常量池)都在元空间
JDK8之后的堆内存模型:
这个元空间:逻辑上存在,物理上不存在
堆内存参数调优初识:在IDEA中,VM options中添加几个参数,可以指定最大内存,最小内存,和是否打印GC的详细信息
1
-Xms8m -Xmx8m -XX:+PrintGCDetails
相关题目
- JVM的内存模型和分区~详细到每个区都放什么?
- 堆里面的分区有哪些?Eden,from,to,老年区,说说他们的特点?
- GC的算法有哪些:标记清除法,标记整理,引用计数法
- 轻GC和中GC分别在什么时候发生?
|
|