JAVA虚拟机内存管理+GC回收内存

首先明确一点,JAVA虚拟机是个什么玩意?
很显然,先不去管JAVA的虚拟机,它是个虚拟机,那就是一个仿真计算机。

1、来看看其内存的访问操作:

在这里插入图片描述
也就是说,JAVA中使用多线程机制,使得多个线程同时执行不同任务,所有线程共享main memory主存,而每个线程又都有自己单独的工作内存,当线程与内存区域进行交互时,数据由主存拷贝到工作内存,进而交给线程处理。

2、我们再来看看JAVA虚拟机的逻辑内存模型:

在这里插入图片描述

  1. 方法区:是线程共享的内存区域,它用来存储已被系统加载的类信息,常量和静态变量,即时编译器编译后的代码等数据,在GC回收内存的分代算法中,java1.7的时候方法区也叫永久代,也就是说方法区中这部分内存是很少被GC回收的,主要是针对常量池的回收和类型的卸载,条件非常苛刻(对类型的卸载),java1.8的时候转移到了元空间,也有可能oom。
  2. Java堆:是所有线程共享的内存区域,它是Java虚拟机管理的最大的一块内存,在虚拟机启动时创建,用来存储new的对象实例,Java堆是GC回收的主要区域,从内存回收的角度来看(如今大多数回收算法都是分代算法):Java堆可以细分为新生代,老年代,再细致一点:Eden 空间、From Survivor 空间、To Survivor 空间等。从内存分配的角度来说,线程共享的Java堆可以区分为多个线程分配缓冲区,根据Java虚拟机规范可知,Java堆上分配内存可以是物理上不连续的内存碎片,如果在Java堆中无法扩充,不能完成对象实例化的分配,就会抛出oom异常,java1.8的时候,运行常量池放到堆里边存放了,之前是在方法区中。
  3. Java虚拟机栈:创建线程时创建Java虚拟机栈,用来存储栈帧,所以它是线程私有的,当Java程序在执行时,会创建一个栈帧,用来存储方法运行时产生的临时数据和中间结果,这些数据包括:局部变量表、操作数栈、动态链接、方法出口等,局部变量表存放了编译期可知的八种数据基本数据类型,局部变量表所需的内存是在编译期间完成分配的,当进入一个方法时,局部变量表所需的内存大小是完全固定的,方法运行期间不会改变,根据Java虚拟机规范,在这个区域规定了两种内存异常:stackOverFlowError和oom,前者是线程请求栈深度大于jvm规定深度(递归会报这个异常),后者是程序扩展无法申请到足够的内存
  4. 程序计数器:它的作用是记录当前线程运行的位置,每个线程都有独立的程序计数器,其工作原理就是通过改变程序计数器的值来选取下一条需要执行的字节码指令,分支、循环跳转、异常处理、线程的阻塞、挂起、恢复都需要程序计数器的参与
  5. 本地方法栈:它的主要作用是处理JNI调用的Native方法,也有两种内存异常:stackOverFlowError和oom

3,GC回收机制

  • 哪些内存需要回收–who
  • 什么时候回收 --when
  • 怎么回收 --how
    ①哪些内存需要回收?
    线程私有的有:程序计数器、jvm栈、本地方法栈(随线程的生命周期,线程分配多少内存是有定数的,当线程被销毁时,内存就会释放)
    线程共享的有:Java堆,方法区(内存都是动态分配的,所以也需要动态回收,这部分是GC回收的主要工作区域)
    ②什么时候回收?
    一个是引用计数法:给对象添加一个引用计数器,每当程序引用一次对象,计数器加一,反之每当一个引用计数器失效时,计数器减一,当计数器为0时,说明当前对象没有引用,举个例子:
Object obj = new Object();//计数器 +1 = 1
obj = null;//计数器 - 1 = 0,失效 GC回收
//但是当对象之间相互调用,引用计数器就无法使GC回收,
Object a = new Object(); // a的引用计数为1 
Object b = new Object(); // b的引用计数为1 
a.next = b; // a的引用计数为2 
b.next = a; // b的引用计数为2 
a = null; // a的引用计数为1,尽管已经显示地将a赋值为null,但是由于引用计数为1,GC无法回收a
b = null; // b的引用计数为1,同理,GC也不回收b

一个是可达性分析:设立若干根对象(GC Root) ,每个对象都是一个子节点,当一个对象找不到根节点,也就是无人引用时,标志其不可达,
可以作为GC Root的对象包括:

  • jvm栈中引用的对象
  • 方法区中静态变量引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中引用的对象
    ③怎么回收?
    标记 - 清除算法(现在升级的算法是标记 - 整理算法)、复制算法、分代算法
    一:标记清除算法,就是遍历GC Root,标记可达不可达对象,然后回收不可达对象,这种算法缺点是效率低,无法回收连续物理内存,后来升级为标记 - 整理算法,将可达对象移动到内存的一端,然后GC回收剩下部分连续的物理内存。
    二:复制算法,最初是将内存分为相等的两块,只用其中的一块,当这块内存满的时候,将其中存活的对象复制到另一块内存中,然后GC 回收释放之前这块内存。
    三:分代算法,在Java中,将内存中的对象按照生命周期长短分成
  • 新生代,活不了多久就死的对象,比如局部变量,用复制算法
  • 老年代 ,生命周期长的对象,活的久不过也是会死的,用标记清除算法
  • 永久代 -----基本上GC不回收

4,垃圾回收器

一、CMS是一种获取最短GC停顿时间为目的,GC线程和用户线程并发操作的经典垃圾回收器,它的特点是低延迟,主要回收老年代的内存区域,使用标记清除(mark-sweep),过程大概是四个阶段:

  • 初始标记:(stop the world事件发生,CPU停顿时间很短)初始标记仅标记GC Root能直接关联到的对象
  • 并发标记:耗时长,但是GC线程与用户线程并行执行,并发标记的过程就是进行 RG Root Tracking
  • 重新标记:(stop the world事件发生,时间很短,但比初始标记长),它的作用是:修正在并发标记的过程中因程序继续执行而导致标记产生变动的那一部分对象的标记记录
  • 并发清除-标记清除算法:耗时长,但是与用户线程并行执行
    二、G1是一款面向服务端应用的垃圾收集器
  • 初始标记,stop the world事件发生,CPU停顿只处理垃圾
  • 并发标记,与用户线程并发执行
  • 最终标记,stop the world 事件发生,CPU停顿只处理垃圾
  • 筛选回收(stop the world事件 根据用户期望的GC停顿时间回收)(注意:CMS 在这一步不需要stop the world)(阿里问为何停顿时间可以设置,参考:G1 垃圾收集器架构和如何做到可预测的停顿(阿里)

问题1:CMS和G1 stop the world(暂停用户线程) 区别?
首先需要明白,什么是stop the world?暂停用户线程,只有GC相关的线程在工作,中断的线程只有GC任务结束后才能继续执行,相较于CMS,G1有如下几点优势:

  • G1可以充分利用CPU多核环境下的硬件优势,使用多个CPU来缩短stop the world 停顿时间,其他一些GC回收器需要暂停用户线程来进行GC的回收,但是G1回收器仍然可以通过多线程并发让java线程继续执行
  • 分代收集:虽然G1可以不需要其他收集器配合就能独立管理整个GC堆,但是还是保留了分代的概念。它能够采用不同的方式去处理新创建的对象和已经存活了一段时间,熬过多次GC的旧对象以获取更好的收集效果
  • 空间整合:与CMS的“标记–清理”算法不同,G1从整体来看是基于“标记整理”算法实现的收集器;从局部上来看是基于“复制”算法实现的。
  • 可预测的停顿:这是G1相对于CMS的另一个大优势,降低停顿时间是G1和CMS共同的关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,

上面几个步骤的运作过程和CMS有很多相似之处。初始标记阶段仅仅只是标记一下GC Roots能直接关联到的对象,并且修改TAMS的值,让下一个阶段用户程序并发运行时,能在正确可用的Region中创建新对象,这一阶段需要停顿线程,但是耗时很短,并发标记阶段是从GC Root开始对堆中对象进行可达性分析,找出存活的对象,这阶段时耗时较长,但可与用户程序并发执行。而最终标记阶段则是为了修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录,虚拟机将这段时间对象变化记录在线程Remenbered Set Logs里面,最终标记阶段需要把Remembered Set Logs的数据合并到Remembered Set Logs里面,最终标记阶段需要把Remembered Set Logs的数据合并到Remembered Set中,这一阶段需要停顿线程,但是可并行执行。最后在筛选回收阶段首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值