目录

  • JVM内存结构
  • JVM内存分配机制
  • 对象回收判断机制
    • 引用计数法
    • 可达性分析算法
  • 垃圾回收算法
    • 标记-复制
    • 标记-清除
    • 标记-整理
  • 垃圾回收器
    • serial(-XX:+UseSerialGC -XX:+UseSerialOldGC)
    • Parallel Scavenge(-XX:+UseParallelGC(年轻代),-XX:+UseParallelOldGC(老年代))
    • ParNew收集器(-XX:+UseParNewGC)
    • CMS收集器(-XX:+UseConcMarkSweepGC(old))
    • G1收集器(-XX:+UseG1GC)
    • ZGC收集器(-XX:+UseZGC)

JVM内存结构

JVM内存结构图
栈帧结构图

jvm内存结构指的是java 虚拟机运行时对内存的使用情况,jvm将内存分为堆、栈、方法区、程序计数器四个部分,栈分为虚拟机栈、本地方法栈

程序计数器(Program Counter Register):jvm会给每个线程都分配一个程序计数器,是线程的私有内存,记录了当前线程将要执行的下一条字节码指令的指针,如果执行的是native方法,程序计数器的值为空,程序计数器不会出现OOM

虚拟机栈(VM Stack):虚拟机栈是线程运行时给线程分配的内存空间,这块内存空间是线程私有的,栈中存储的元素叫栈帧,线程在执行方法时会创建一个栈帧,栈帧入栈和出栈的过程对应着方法的调用到执行完毕,栈帧中存储局部变量表、操作数栈、动态链接、方法出口等信息

局部变量表:存储了基本数据类型的局部变量和对象的引用,在编译期间就决定了所占空间的大小
操作数栈:方法执行时的算术运算或方法调用的参数传递是通过操作数据栈进行的,可以理解为算术运算时数据的临时存储区
动态链接:栈帧中包含一个运行时常量池中该栈帧所属方法的引用
方法出口:方法调用时的位置,方法退出时回到该位置,并返回值(如果需要返回值)

方法的递归调用可能会导致栈内存溢出:StackOverflowError, 使用-Xss分配栈内存大小
如果CPU占用过高的排查办法:
步骤1:top命令查看进程的cpu占用情况,锁定进程id
步骤2:用如下命令进一步定位占用出问题的线程id:
ps H -eo pid,tid,%cpu | grep 进程id
步骤3:将10进制的线程id转成16进制,比如32665 ==> 7F99
步骤4:使用jstack命令查看进程信息,根据线程id 7F99查找线程,可看到线程的详细信息,进而定位出问题代码的行数 jstack 进程id

本地方法栈(Native Method Stack):JVM调用本地方法时的内存空间,本地方法底层会调用C、C++等语音直接和操作系统交互

堆(Heap):new 出来的对象会放在堆里,堆内存时线程共享的,垃圾回收器就是用各种算法回收堆中不再使用的对象(实际是回收了对象所在的内存空间)
堆内存不够分配对象可能会引起溢出:java.lang.OutOfMemoryError:java heap space
可以用-Xms分配初始堆内存的大小,-Xmx控制堆内存最大的大小

方法区(Method Area):方法区的内存空间也是被所有线程共享的,存储类的相关信息:类的成员变量、常量、方法等等即类编译后的代码,同时还包含运行时常量池,JDK1.8以后使用元空间(MetaSpace)代替方法区,元空间的内存空间是在本地内存分配的,方法区也会出现内存溢出
使用-XX:MetaSpaceSize控制元空间大小,-XX:MaxMetaspaceSize限制元空间最大占用空间
常量池存储编译器生成的字面量和引用量,字面量包含字符串(String a = "abc")、基本数据类型的值(int a = "10")、final修饰的常量,引用量就是符号引用,包含类和接口的全限定名、字段的名称和描述符、方法的名称和描述符 ,常量池可以避免频繁的创建和销毁可以共享的对象
Integer常量池范围[-128,127]

JVM内存分配机制

根据对象的生存周期,堆内存可以分为新生代和老年代,新生代又可以细分为Eden、SurvivorFrom(s1)、ServivorTo(s2)区,三者的比例默认为8:1:1(可以通过‐XX:SurvivorRatio参数调整),大多数的对象首先被分配到新生代中的Eden区,当Eden区没有足够的空间时触发Mionr GC/Young GC,大部分的对象被回收,剩下的存活对象转移到s1区,存活对象的年龄+1,等下一次Eden区满了后又触发minor gc,存活对象转移s2区,年龄再+1,等年龄达到15(默认值)就分配到老年代,老年代空间不足时触发Major GC/Full GC,一般会回收老年代、年轻代、方法区的垃圾,Majro GC的速度一般比Minor GC慢10倍以上,所以要尽量避免频繁的Full GC,JVM可以通过逃逸分析来确定该对象不会被外部访问,如果不会逃逸可以将该对象在栈上分配内存

对象分配到老年代的情况有以下几种:
1、大对象直接进入老年代:需要大量连续内存空间的对象(如:字符串、数组)会直接分配到老年代,使用-XX:PretenureSizeThreshold可以设置大对象的大小,这个参数只在Serial和ParNew收集器中生效

2、长期存活的对象进入老年代:存活年龄达到15岁(CMS默认是6岁)的对象将会进入老年代,可以用-XX:MaxTenuringThreshold参数控制年龄阈值

3、对象动态年龄判断:当前放对象的survivor区域里,一批对象的总大小大于这块survivor区域内存大小的50%(-XX:TargetSurvivorRatio可以指定),大么此时大于等于这批对象年龄最大值的对象分配进老年代,比如survivor区中的对象,年龄1+年龄2+年龄n的对象大小总和超过了survivor区域的50%,那么把年龄n(包含n)以上的对象放进老年代,对象动态年龄判断机制一般是在minor gc之后触发

4、老年代空间分配担保机制:年轻代每个minor gc之前JVM都会计算老年代剩余可用空间,如果可用空间小于年轻代里现在存活的所有对象大小之和(包括垃圾对象),看是否设置了-XX:-HandlePromotionFailure参数(JDK8默认设置),如果设置了这个参数,就会看老年代的可用内存是否大于之前每一次minor gc后进入老年代的对象的平均大小,如果大于则进行minor gc,如果小于或者没有设置参数,就触发full gc,老年代和新生代一起回收一次垃圾,如果回收完还是没有足够的空间存放新分配进来的对象就会发生OOM,即使大于平均大小,如果minor gc后剩余存活对象的大小还是大于老年代可用空间也会触发full gc,full gc完成后如果还是没有足够的空间也会发生OOM

对象回收判断机制

引用计数法

给对象添加一个引用计数器,每当有一个地方引用就加1,不再引用就减1,计数器为0说明对象不再被使用,可以回收,缺点是很难解决对象之间相互循环引用的问题

可达性分析算法

将GC Roots对象作为起点,从这些节点开始向下搜索引用的对象,找到的对象都标记为非垃圾对象,其他未标记的对象都是垃圾对象
GC Roots:线程栈的本地变量、静态变量、本地方法栈的变量等

常见的引用类型:强引用、软引用、弱引用、虚引用

即使在可达性分析算法中不可达的对象也不一定会被回收,至少要经历再次标记过程。标记的前提是对象在进行可达性分析后发现没有与GC Roots相连接的引用链,如果该对象覆盖了finalize方法,可以在finalize方法中重新与引用链上的任意一个对象建立关联,那么就会移除出回收的集合,一个对象的finalize方法只会被执行一次

垃圾回收算法

标记-复制

标记-复制算法将内存空间分为大小相同的两个部分,分配内存空间时只使用其中一个部分,当内存使用完后就将存活的对象复制到另一半空间,并清理标记的对象,常用于新生代

标记-清除

标记存活的对象,统一清理掉未被标记的对象,或者标记需要被清理的对象,标记完成后统一清理被标记的对象,缺点是会产生不连续的内存碎片

标记-整理

类似标记清除算法,标记完成后将存活对象放到一端,然后清理掉端边界以外的对象,这样就完成了对空间的整理

垃圾回收器

serial(-XX:+UseSerialGC -XX:+UseSerialOldGC)

串行收集器,使用单线程完成垃圾收集,在收集垃圾时必须暂停其他工作线程(STOP THE WORLD),新生代采用复制算法,老年代采用标记-整理算法,serial的优点在于单线程效率高,serail old收集器是serial的老年代版本,主要用于与Parallel Scavenge收集器配合使用,和作为CMS收集器的后备方案

Parallel Scavenge(-XX:+UseParallelGC(年轻代),-XX:+UseParallelOldGC(老年代))

parallel收集器就是serial收集器的多线程版本,默认的线程数和CPU核数相同,Parallel Scavenge收集器关注的是吞吐量(CPU的利用效率),而CMS收集器关注的是应用线程的停顿时间(体验更好),新生代采用复制算法,老年代采用标记-整理算法,Parallel Old收集器是Parallel Scavenge收集器的老年代版本,JDK8默认的新生代和老年代垃圾收集器就是Parallel Scavenge和Parallel Old

ParNew收集器(-XX:+UseParNewGC)

ParNew收集和paraller收集器类似,新生代采用复制算法,老年代采用标记-整理算法,主要是可以和CMS收集器配合使用

CMS收集器(-XX:+UseConcMarkSweepGC(old))

CMS收集器关注于将停顿时间尽量缩短,是HotSpot虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程 (基本上)同时工作,CMS基于标记-清除算法实现,整个过程分为4个部分:
1、初始标记:暂停其他所有线程(此时会STW),并记录下GC ROOT能直接引用的对象,耗时很短
2、并发标记:从GC ROOT的直接关联对象开始遍历整个对象引用,此阶段不会STW,而是和用户线程并发进行,所以可能会导致已标记过的对象状态发生变化
3、重新标记:此阶段会修正并发标记阶段状态发生变化的对象,此阶段会STW,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短,主要用三色标记里的增量更新算法做重新标记
4、并发清理:开启用户线程,同时GC开始垃圾收集,此阶段的新增对象会标记为黑色不做处理
5、并发重置:重置GC过程中的标记数据

可以看到CMS的优点在于可以并发收集、停顿时间短,缺点在于并发线程需要耗费一定的CPU资源、无法处理在并发标记和并发清理阶段产生的垃圾,标记清除算法会产生内存碎片(可以用XX:+UseCMSCompactAtFullCollection参数控制清理完毕后做整理),会出现在垃圾收集的过程中又触发垃圾回收的情况,尤其是在并发标记和并发清理阶段还没回收完就又触发full gc,此时会STW,用serail old来回收

G1收集器(-XX:+UseG1GC)

G1主要适用于配备多核心处理器及大容量内存的机器,既满足GC停顿时间的同时又兼顾高吞吐量,G1弱化了新生代和老年代的概念而强化分区的概念,G1将堆分为大小相同的独立区域(Region),JVM最多有2048个Region,G1中的年轻代和老年代不在是连续的空间而是Region的集合,默认Region中的5%是年轻代,最多不超过60%,年轻代中Eden和Survivor对应的Region比例默认也是8:1:1,一个Region之前可能是年轻代,垃圾回收又可能变成老年代,G1的老年代分配机制和之前的垃圾回收器相同,不同点在于对大对象的处理,如果一个对象的大小超过了一个Region大小的50%,那么就会被放进Humongous中,Humongous是专门划分出来分配大对象的Region区域

G1收集器一次GC的过程大概如下:
1、初始标记:STW,记录下gc root直接引用的对象
2、并发标记:同CMS的并发标记
3、最终标记:STW,同CMS的重新标记
4、筛选回收:STW,筛选回收阶段首先对各个Region的回收价值和成本进行排序,根据用户期望的GC停顿时间(可以用-XX:MaxGCPauseMillis指定)来指定回收计划,即将回收时间控制在用户期望的时间之内,回收算法主要用复制算法,将一个Region中的存活对象复制到另外一个Region中,这种不会像CMS那样回收完还有很多内存碎片,在Shenandoah中实现了筛选回收的并发执行,G1收集器在后台维护了一个收集的优先级列表,每次回收价值最大的Region,即耗时短回收的空间又大

Young GC:区别于其他收集器Eden区不足时触发,G1会计算Eden回收大概需要多次时间如果远小于-XX:MaxGCPauseMills设定的值,则继续增加年轻代的Region,直到下一次Eden区放满,回收时间接近-XX:MaxGCPauseMills设定的值触发Young GC
Mixed GC:老年代的堆占有率达到-XX:InitiatingHeapOccupancyPercent设定的值则触发,回收所有的Young和部分的Old以及大对象区域,主要使用复制算法,把存活对象拷贝到其他的Region中,如果Region空间不足则触发Full GC
Full GC:STW,然后用单线程标记-整理回收垃圾

ZGC收集器(-XX:+UseZGC)

ZGC是JDK11中新加入的具有实验性的低延迟垃圾收集器,ZGC的主要目标有:

  • 支持TB量级的堆
  • 最大GC停顿时间不超过10ms
  • 奠定未来GC特性的基础
  • 最糟糕的情况下吞吐量会降低15%

最大的优点是停顿时间不会随堆的大小或活跃对象的大小的增长而增长

不分代:
ZGC目前还没有实现分代机制

ZGC内存布局:
ZGC基于Region内存布局,暂时无分代,使用了读屏障和颜色指针等技术实现可并发的标记-整理算法

连线之间的垃圾收集器可以搭配使用 JDK1.8默认使用的垃圾收集器是Parallel JDK1.9默认使用G1垃圾收集器

JVM内存结构和垃圾回收机制相关推荐

  1. JVM原理(Java代码编译和执行的整个过程+JVM内存管理及垃圾回收机制)

    转载注明出处: http://blog.csdn.net/cutesource/article/details/5904501 JVM工作原理和特点主要是指操作系统装入JVM是通过jdk中Java.e ...

  2. 详解JVM内存管理与垃圾回收机制2 - 何为垃圾

    随着编程语言的发展,GC的功能不断增强,性能也不断提高,作为语言背后的无名英雄,GC离我们的工作似乎越来越远.作为Java程序员,对这一点也许会有更深的体会,我们不需要了解太多与GC相关的知识,就能很 ...

  3. 详解JVM内存管理与垃圾回收机制5 - Java中的4种引用类型

    在Java语言中,除了基础数据类型的变量以外,其他的都是引用类型,指向各种不同的对象.在前文我们也已经知道,Java中的引用可以是认为对指针的封装,这个指针中存储的值代表的是另外一块内存的起始地址(对 ...

  4. Java内存结构与垃圾回收机制算法分析

    什么是HotSpot VM 提起HotSpot VM,相信所有Java程序员都知道,它是Sun JDK和OpenJDK中所带的虚拟机,也是目前使用范围最广的Java虚拟机. 但不一定所有人都知道的是, ...

  5. 学习笔记:Java虚拟机——JVM内存结构、垃圾回收、类加载与字节码技术

    学习视频来源:https://www.bilibili.com/video/BV1yE411Z7AP Java类加载机制与ClassLoader详解推荐文章:https://yichun.blog.c ...

  6. JVM内存模型和垃圾回收机制

    JVM内存模型 根据Java虚拟机规范,Java数据区域分为五大数据区域. 其中方法区和堆是所有线程共享的,虚拟机栈.本地方法栈和程序计数器则为线程私有的. 有的博客称方法区是永久代,那是因为前者是J ...

  7. JVM内存结构、垃圾回收那点事

    翻看电脑的文件夹,无意看到了9月份在公司做的一次分享,浏览了一下"婆婆特",发现自己在ppt上的写的引导性问题自己也不能确切的回答出来,哎,知识这东西,平时不常用的没些日子就生疏了 ...

  8. JVM内存结构和垃圾回收算法

      jvm的内存结构:按照线程划分可分为两部分,一是线程独占的,二是线程共享的.线程共享的有方法区和堆,线程独占的有程序计数器,本地方法栈,虚拟机栈.方法区是一个模型规范,具体实现的话,是元空间和永久 ...

  9. visual studio内存溢出检测工具_详解JVM内存管理与垃圾回收机制2 何为垃圾

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 随着编程语言的发展,GC的功能不断增强,性能也不断提高,作为语言背 ...

最新文章

  1. android WebView中js的alert()失效
  2. javaweb添加拦截器
  3. java金钱千分位处理_java对金额的处理,保留两位小数、千分位符
  4. Qt程序运行提示“it could not find or load the QT platform plugin “windows””
  5. SDUT-SQL题解
  6. Homework 8 测试计划
  7. Window7+vs2008+QT环境搭建
  8. servlet核心API的UML图
  9. 你真的了解静态变量、常量的存储位置吗?
  10. ux和ui_使用UX设计师为Amazon的Alexa学习会话式UI的基础
  11. 《Http Client 官方文档》7. 高级主题
  12. Java从零开始学十五(继承)
  13. 算法——Java实现栈
  14. [转]史上最全Eclipse优化
  15. UCGUI信息处理机制
  16. 树莓派系统备份与还原
  17. GeoServer中的WPS服务-概念
  18. java 中vo、po、dto、bo、pojo、entity、mode如何区分
  19. webpack4 学习时打包图片时遇到的问题
  20. 死理性派恋爱法:拒绝掉前面37%的人

热门文章

  1. 各大浏览器兼容性报告 IE、FF、Safari、OP不同浏览器兼容报告
  2. JPA设置表名和实体名,表字段与实体字段的对应
  3. python画统计图
  4. 开发资源总结 (转载)
  5. 题解 CF1395A 【Boboniu Likes to Color Balls】
  6. 数字图像处理学习笔记(三)
  7. module ‘win32com.gen_py.00020813-0000-0000-C000-000000000046x0x1x9‘ has no attribute ‘CLSIDToClassM
  8. 紫薯第9章动态规划,从入门到入土, dp 它tnl(背包代码模板部分)
  9. Crowd Counting领域论文阅读
  10. 组装微型计算机需要哪些硬件设备,我们需要哪些配件组装电脑