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


       第一部分、闲扯+概述

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

    要理解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还没有实际动手通过相关知识解决过项目中的问题,目前只是纸上谈兵,后面会摸索一下如何通过工具监控项目中的内存状况,并整理出来。学习之

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

  • 相关阅读:
    JVM知识体系
    RabbitMQ学习11死信队列(拒绝消息)
    JUC知识体系
    RabbitMQ学习10死信队列(队列达到最大长度)
    Dropdownlist+objectdatasource设定“请选择”默认选项
    sql DATEPART函数使用
    win7 'IIS APPPOOL\Classic .NET AppPool' 登录失败
    sqlserver2000还原数据库时报设备激活错误的解决方法
    vss和vs2008组合搭建源代码管理器
    在配置win7 IIS浏览网站时 检测到在集成的托管管道模式下不适用的ASP.NET设置 的解决方法
  • 原文地址:https://www.cnblogs.com/zzq6032010/p/10093603.html
Copyright © 2020-2023  润新知