JVM内存区域划分及垃圾回收

时间:2022-06-06 13:00:14

第一部分、闲扯+概述

近来在研读《深入理解java虚拟机》一书,读完之后做个小结,算是记录一下自己的学习所得,在成长的路上,只能死磕。

要理解JVM,就要先从其内存区域划分开始,知道其由几部分构成,再了解各部分的功能,这样就能对其整体有一个了解。

话不多说,总体图先呈上:

JVM内存区域划分及垃圾回收

可以看到,线程私有的内存区域有虚拟机栈、本地方法栈、程序计数器,这些区域都不会出现线程安全问题;而线程共享的区域有堆、方法区。

下面对其各个分区域进行介绍。

第二部分:JVM内存区域划分

1、首先是最简单的程序计数器,每个线程都有一个独立的计数器,用来记录线程执行的虚拟机字节码指令。另外,如果是Native方法,则计数器中的

值为undifined。

2、本地方法栈:类似于虚拟机栈,只是此处运行的是native方法

3、虚拟机栈:运行java方法的地方,基本单位是栈帧,一个方法从开始执行到结束的过程,就是一个栈帧在虚拟机栈中入栈到出栈的过程。(本来

认真准备了两张栈帧跟局部变量表的逻辑示意图,不知道怎么的传不上来,只能省去,<手动擦汗>)

3.1、局部变量表:顾名思义,是存返方法中变量的地方。

局部变量表存放变量的单位是Slot 槽,每个槽都能存放一个32位的变量,64位的由连续的两个槽来存放。如果执行的是非static方法,则第一个槽中

存放的是当前对象的引用,即this。具体存放的引用,是直接或间接能定位到这个对象在堆中的位置的一个值,比如对象的起始地址。

3.2、操作数栈:是LIFO(后入先出)栈,虚拟机就是一个基于操作数栈的执行引擎。在处理数据中处于重要位置。一个32位的数据占据栈中一个容量。比如执行a+b时,字节码

指令会先往栈中压入a跟b,然后iadd指令执行时,将栈顶的两个值a跟b弹出,相加,再压入栈中,完成计算。

3.3、动态链接:存放的是方法区的运行时常量池中此栈帧对应的方法的引用

3.4、返回地址:记录方法执行完之后返回的位置,回到这个位置程序才能正确的继续执行下去。

4、方法区:最主要的功能,便是存放字节码文件、Class对象、常量等

5、堆区:此部分是虚拟机中上连栈下连方法区的中枢之地,也是垃圾收集的重地,可以说基本所有的对象都存放在此区域。

   对象在堆区中,由三部分组成:对象头、实体数据、对齐填充

对象头:一般由两部分组成,一部分是用于存放hash值、分代年龄、锁标识的称为Mark Word的区域,另一部分用于存放指向方法区中对象类型的指针(直接指针定位时有);

实体数据:存放正常的对象数据;

对齐填充:对象的内存占用大小为8字节的整数倍,如果不够,用此部分来填充。

堆区按照垃圾收集区域,划分为两部分:新生代、老年代,一般默认比例是1:2,而在新生代中,又分成了三部分,分别为Eden、Survivor From 、Survivor To ,默认比例

为8:1:1。

第三部分、垃圾回收

触发条件:当内存中可用空间不够时才会触发垃圾回收

什么样的对象会被回收:通过可达性分析算法,如果发现某个对象与GC ROOTS没有连接,则此对象满足被回收的条件。

GC ROOTS定义:1) 虚拟机栈中引用的对象;2)本地方法栈中引用的对象;3) 类静态属性引用的对象;4) 常量引用的对象

如何回收:标记清除算法、复制算法、标记整理算法、分代搜集算法,目前主流虚拟机中使用的就是分代搜集算法

分代搜集算法的过程:JVM开发团队发现,对于新生代中大部分的对象,存活时间都很短,所以新生代使用复制算法,且工作空间与空闲空间之比为9:1(9即上面所说的一个

Eden与Survivor From之和),因为新生代绝大部分对象会被清理,清理之后还剩下的对象就放在那一份的Survivor To中,如果空间不够,再往老年代放,另外经历过一定次数的

新生代处理还没被回收的对象,也会转移到老年代中;而对于老年代,用标记整理算法或者标记清除算法,因为老年代对象数量庞杂,且大多数不满足清理条件,所以老年代的

垃圾回收耗时长效果相对较差。新生代中Eden区域专门用于存放新对象,如果此区域内存空间不足,则触发新生代GC,此之谓Minor GC;同样老年代空间不足,触发Full GC(亦

称之为Major GC),耗时长,要尽量避免,一般来说Full GC次数一天不能超过一次。

垃圾收集器:针对新生代跟老年代都有不同的垃圾搜集器,例如针对新生代的有Serial、ParNew等,针对老年代的有CMS、Serial Old等,不同的搜集器之间只能有特定的

几个组合(不能随意地组合使用,会有不兼容),具体看项目实际情况而定。目前BZ所在公司用的是ParNew与CMS的组合,虽然CMS用的是标记清除算法,会使垃圾收集后的内存

碎片较多,但是万能的前辈们早就想到了这些,提供了-XX:+UseCMSCompactAtFullCollection指令压缩内存中的碎片,以及-XX:CMSFullGCsBeforeCompaction指令设置多少次

Full GC后整理内存碎片。

针对垃圾收集这一块,BZ还没有实际动手通过相关知识解决过项目中的问题,目前只是纸上谈兵,后面会摸索一下如何通过工具监控项目中的内存状况,并整理出来。学习之

路,还是要多给自己打打鸡血~