浅析JVM内存模型及垃圾回收算法

1.JVM内存模型

1.1内存模型

image.png

1.2 名词解释

方法区:称为永久代区域,java虚拟机规范中把方法区描述为堆的一个逻辑部分,但是为了和Heap(堆区)对应,也称Non-Heap(非堆区)。主要存储的是静态变量,常量(包括运行时常量),类的加载信息和java编译后的代码。 JDK1.8之后,这块名词发什么改变,叫做:Metaspace 元数据空间。

堆区:存储的都是对象的实例和数组对象。

虚拟机栈:多线程使用,方法区自己定义的对象等相关信息。

本地方法栈:和虚拟机栈几乎一样,HotSpot甚至把这两块合二为一。

程序计数器:贼底层的东西,应用工程师可以不用关注,也是是JVM中唯一不会报内存溢出
(OutOfMemoryError)的区域。

2.JVM如何垃圾回收的

2.1 垃圾回收算法

2.2.1 复制算法

简单的说,就是把一个内存空间,一分为二,一块使用,一块用于垃圾回收。当代码在不停的运行的时候,大量对象会放在其中之一(其中有大量垃圾无用对象和零散空间),一段时间之后,把该内存有效对象提出到另一块内存,然后清空该内存块,就称之为复制算法。

这样的缺点很明显,内存的使用率只能达到50%,浪费大量内存。

2.2.2 复制算法优化V2.0版本

1个Eden区,2个Survivor区,其中Eden占80%,Survivor各占10%。
刚开始对象都分配在Eden区,如果Eden区快满了,则执行垃圾回收机制。把Eden区有效的对象都放入到一个Survivor区中,其中另一个Survivor区放上一次GC存活后的对象。
如果再来一次GC,则把上一次GC存活后的对象survivor区和Eden区的对象放到另一块空闲Survivor区中,然后清空本次Survivor对象,周而复始的执行。

这样显而易见的好处就是,内存的使用率从50%提升到了90%。

哪有什么问题么?

比如:
1.万一垃圾回收后,存活下来的对象超过了10%的内存空间,在另一块Survivor装不下咋整?
2.万一有个贼大的对象过来,大到10%,咋整?

根据上述两个问题,可以明显得出,复制算法优化V2.0版本还不足以解决JVM垃圾回收的问题,所以引入垃圾回收3.0版本。

2.2.3 垃圾回收优化3.0版本

通过复制算法V2.0版本,我们解决了JVM内存及回收普通场景,这块引入到JVM就是对应的是新生代内存模型理论。但是,很显然,JVM内存问题及垃圾回收还远远没有得到彻底解决。此时,再引入一个内存空间,老年代,用于解决新生代解决不了的问题。

老年代内存空间使用的是标记-清除算法,因此,在执行效率上会比新生代的复制垃圾回收慢10倍以上,唯一的优势就是老年代的内存空间可以100%的使用,不会存在浪费现象。

引入老年代之后,然后解决问题:
万一垃圾回收后,存活下来的对象超过了10%的内存空间,在另一块Survivor装不下咋整?
1.在每次新生代minnor GC之前,首先第一步会去计算有效对象的内存空间是多少,如果小于Survivor内存空间,则直接minnor GC,流程结束。
2.如果大于Survivor内存空间,则会去判断老年代连续空闲空间的大小时候大于Survivor内存空间,
3.如果大于,则直接minnor GC,放不下的对象信息,放入老年代即可。
4.如果小于,则会触发老年代的垃圾回收,如果回收之后,内存空间够大,则再触发一次minnor GC,反之,会报OOM异常。

这里还有个小细节,需要注意是否JVM参数中是否配置了handlepromotionfailure,如果handlepromotionfailure参数打开,则上述流程还要再次复杂化。
handlepromotionfailure=true,那垃圾回收的效率会再次提升,但是也会存在相对应的风险,具体为啥,可以百度下handlepromotionfailure=true的含义,本文不做过段阐述。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容