JVM堆内存模型及垃圾回收机制相关简析
一、简介
垃圾回收机制是Java中最重要的保障,而谈到垃圾回收,可以简单的概括为两个问题:
一、在哪里回收?
二、怎么回收?
而对于上面两个问题结合下面我要讲的,这样回答:
一、主要在堆内存模型中回收
二、先判断,后回收
这样可以总结为一句话,在内存模型中先进行判断流程,在进行回收流程,下面在详细解释。
二、堆内存模型
Java1.8之前堆内存逻辑上分为3部分,新生代、老年代、永久区。
新生代又被分为一个伊甸园和两个幸存者区。
并且从Java1.8开始,永久代替换为元空间。
新生代:刚创建的对象存放在伊甸园,每次GC都会将伊甸园的对象转移至幸存区
老年区:幸存区的对象经过15次GC不死就会来到老年代,而当老年区的可用内存小于新生代的可用内存就会触发一次重回收
元空间:jdk8之前为永久代,占用JVM内存,8之后为元空间,要说明的的是,元空间并不是堆的一部分,而是脱胎于方法区,他并不是不会被GC,只是GC的条件比较苛刻。
默认情况下,JVM内存 = 新生代 + 老年代(其他如栈等可忽略不计),为电脑内存的四分之一,初始化内存是六十四分之一。
三、垃圾回收
1、判断阶段
1)引用计数法
当有一个地方引用他时,对象的计数器+1,引用失效就-1,当计数器为0,就表示可以被回收。
主流Java虚拟机不采用它,无法解决对象之间循环依赖问题。
引用分类:
强引用:引用还在,就不会被回收
软引用:发生内存溢出之前,进行回收
弱引用:只能活到下一次垃圾回收之前
虚引用:无法通过虚引用获得对象实例,设置虚引用的唯一目的是对象被回收时能获得一个系统通知
2)可达性分析法
通过一系列被称为 GC Roots 的对象作为起始点,从起始点开始向下搜索,当一个对象到起始点没有任何链相连,证明此对象不可用,即被回收。(按图的说法即为不可达)
3)finalization机制(不推荐)
finalize() 方法,当一个对象被可达性分析中被判断为不可达,那么会被JVM判断是否有必要执行finalize()方法(是否执行过),如果没必要执行,直接死,如果有必要执行,对象被放入F-Queue队列中,在下一次GC标记队列前,没有与起始节点想办法建立连接,则必死无疑。
2、回收阶段
1)标记清除算法
先标记后回收,最基础的回收算法,会产生内存碎片,导致内存泄漏
2)复制算法
两个幸存区的互相复制,来解决内存碎片问题
3)标记整理算法
多用于老年代,让幸存对象向一段移动,然后清理掉到端外的对象
4)分代收集算法
目前所有GC都采用分代收集算法进行回收,新生代用复制,老年代用标记整理。
5)增量收集算法
基于GC时所有应用程序的线程都会挂起,所以当GC时间过长,会造成系统长时间停顿,因此,每次GC,只收集一小部分内存的垃圾,然后切换回应用程序线程,及能一次完成的工作,分多次完成,增加了工作量,减少了系统的停顿时间,但是频繁线程切换、上下文转换,导致垃圾回收总成本上升。
6)分区算法
还是为了解决回收停顿过长的问题,将堆内存分为一个个独立使用、独立回收的小空间,从而减少一次GC所停顿的时间,并且可以控制一次回收多少个小空间。
四、相关问题
1、内存泄漏
已分配的内存由于某种情况未释放或无法释放,导致系统运行变慢甚至崩溃
2、内存溢出
即一次性从数据库读取的数据太多,超过了内存的容量,通俗说就是任何导致内存超出的情况
五、垃圾回收器
评价一个垃圾回收器的性能指标:
吞吐量
停顿时间
内存占用
收集频率
收集开销
速度