前言
在这篇文章介绍并记录关于JVM内存结构的简单应用学习。基于JDK8。
1.JVM内存结构
1.1运行时数据区

1.2 程序计算器PC Register
JVM支持多线程同时执行,每一个线程都有自己的PC Register,线程正在执行的方法叫做当前方法,如果是Java代码,PC Register 里面存放的就是当前正在执行的指令地址,如果是C代码,则为空
1.3 虚拟机栈JVM Stacks
Java 虚拟机栈(Java Virtual Machine Stacks)是线程私有的,它的,生命周期与线程是相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧,用于存储局部变量量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应一个栈帧在虚拟机栈中入栈到出栈的过程。
1.4 堆Heap
Java堆(Java Heap)是Java虚拟机所管理的内存中是最大的一块。堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。
Java堆可以处理物理上不连续的内存空间中,只要逻辑上市连续的即可。
1.5 方法区Method Area
方法区与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做No-Heap(非堆),目的是与Java堆区分开来。
1.6 常量池Run-Time Constant Pool
运行时常量池(Run-Time Constant Pool)是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外、还有一项信息是常量池( Constant Pool Table),用于存放编译期生成的各种字面量和符号引用(int、数组),这部分内容将在类加载后进入方法区的运行时常量池中存放。
1.7 本地方法栈Native Method Statcks
本地方法栈与虚拟机栈所发挥的作用是非常相似的,它们之间的区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到Native方法服务。
1.8 JVM 内存结构

1.8.1 年轻代
所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。年轻代分三个区。一个Eden区,两个 Survivor区(一般而言)。大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当这个 Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当这个Survivor去也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制“年老区(Tenured)”。需要注意,Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时存在从Eden复制过来对象,和从前一个Survivor复制过来的对象,而复制到年老区的只有从第一个Survivor去过来的对象。而且,Survivor区总有一个是空的。同时,根据程序需要,Survivor区是可以配置为多个的(多于两个),这样可以增加对象在年轻代中的存在时间,减少被放到年老代的可能。
1.8.2 年老代
在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。
1.8.3 持久代
用于存放静态文件,如今Java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate 等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过-XX:MaxPermSize=<N>进行设置。
1.8.4 Metaspace
Metaspace会有Class、Package、Method、Field、字节码、常量池、符号引用等等。
CSS:32位指针的Class
CodeCache:JIT编译后的本地代码、JNI使用的C代码
1.9 压缩站空间
压缩站空间指的是CCS,默认是存在压缩站空间。

1.9.1 演示禁用压缩站
禁用后就看到不CCSC CCSU了,只显示为0了
vim catalina.sh

1.9.2 常用参数
//堆大小
-Xms -Xmx
//新生代大小,最大的大小
-XX:NewSize -XX:MaxNewSize
// 新生代和老年带的比例, Eden和survivo比例大小
-XX:NewRatio -XX:SurvivorRatio
//元空间
-XX:MetaspaceSize -XX:MaxMetaspaceSize
//开启压缩站 启动有CCS
-XX:+UseCompressedClassPointers
// 没用CCS
-XX:CompressedClassPointers
-XX:InitialCodeCacheSize // CodeCache最初大小
-XX:ReservedCodeCacheSize // 最大的大小 根据不用调
2 垃圾回收算法
思想:枚举根节点,做可达性分析
根节点:类加载器、Thread、虚拟机栈的本地变量表、static成员、常量引用、本地方法栈的变量等等。
2.1 标记清除
算法分为"标记"和"清除"两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有。
缺点:效率不高。标记和清除两个过程的效率都不高。产生碎片。碎片太多会导致会提前GC。
2.2 复制
算法:它将可用内存按容量划分为大小相等的两块,每次 只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另一块上面,然后再把已经使用的内存空间一次清理掉。
优缺点:
实现简单,运行效率高,但是空间利用率低。
2.3 标记整理
算法:标记过程仍然与"标记-清除"算法一样,但后续步骤不是直接对可回收的对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
优缺点: 没有了内存碎片,但是整理内存比较耗时。
2.3 分带垃圾回收
Young区复制算法
Old区用标记清除或者标记整理
2.4 对象分配
对象优先在Eden分配
大对象直接进入老年代: -XX:PretenureSizeThreshold
长期存活对象直接老年代: -XX:MaxTenuringThreshold
-XX:+PrintTenuringDistribution -XX:TargetSurvivorRatio
3 并行VS并发
并行(Parallel): 指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态;
适合科学计算、后台处理等弱交互场景。
并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),用户程序在继续运行,而垃圾收集程序运行于另一个CPU上。
适合对响应时间有要求的场景,比如Web;
3.1 停顿时间 VS 吞吐量
停顿时间: 垃圾收集器做垃圾回收中断应用执行 时间。XX:MaxGCPauseMillis
吞吐量: 花在垃圾收集的时间和花在应用时间 占比。-XX:GCTimeRatio=<n>,垃圾收集时间占:1/1+n
3.2 串行收集器
-XX:UseSerialGC -XX:+UseSerialOldGC
3.3 并行收集器
3.3.1 吞吐量优先
-XX:+UseParallelGC, -XX:+UseParallelOldGC
3.3.2 查看并行收集器

3.4 并发收集器
3.4.1 相应时间优先
CMS: XX:+UseConcMarkSweepGC -XX:+UseParNewGC
3.4.2 开启CMS垃圾回收器命令

3.4.2 查看

3.4.3 垃圾收集器搭配

3.4.3 如何选择垃圾收集器
优先调整堆的大小让服务器自己来选择
如果内存小于100M,使用串行收集器
如果是单核,并且没有停顿时间的要求,串行或者JVM自己选
如果允许停顿时间超过1秒,选择并行或者JVM自己选
如果相应时间最重要,并且不能超过1秒,使用并发收集器
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/collectors.html
3.5 Parallel Conllector
-XX:+UseParallelGC 手动开启 , Server模式开启
-XX:ParallelGCThreads=<N>多少个GC线程
CPU > 8 N = 5/8
CPU < 8 N = CPU
3.6 Parallel Conllector Ergonomics
-XX:MaxGCPauseMillis=<N>
-XX:GCTimeRatio=<N>
-Xmx<N>
3.7 动态内存调整
-XX:YoungGenerationSizelncrement = <Y>
-XX:TenuredGenerationSizeIncrement=<T>
-xx:AdaptiveSizeDecrementScaleFactor=<D>
3.8 CMS Conllector
- 并发收集
- 低停顿,低延迟
- 老年代收集器
4 CMS垃圾收集过程
- CMS initial mark : 初始标记Root,STW
- CMS concurrent mark :并发标记
- CMS-concurrent-preclean: 并发预清理
- CMS remark : 重新标记,STW
- CMS concurrent sweep: 并发清除
- CMS-concurrent-rest:并发重置
4.1 CMS 缺点
- CPU敏感
- 浮动垃圾
- 空间碎片
4.2 CMS 相关参数
-XX:ConcGCThreads:并发的GC线程数
-XX:+UseCMSCompactAtFullCollection:FullGC之后做压缩
-XX:CMSFullGCsBeforeCompaction:多少次FullGC之后压缩一次
-XX:CMSlnitiatingOccupancyFraction:触发FullGC
-XX:+UseCMSlnitiatingOccupancyOnly:是否动态调
XX:+CMSScavengeBeforeRemark:FullGC之前做YGC
-XX:+CMSClassUnloadingEnabled:启动回收Perm区
4.3 ICMS
使用于单核或双核 不用
5 G1 Collector
基于JDK8后的版本、JDK11可能是geely Collector
简介
G1(Garbage-First )收集器是一种server-style 回收器,主要面向多核,大内存的服务器。G1 在实现高吞吐的同时,也最大限度满足了GC 停顿时间可控的目标。在Oracle JDK7 update 4 及 以后的版本全面支持G1 回收器功能。G1收集器主要为有如下需求的程序设计:
可以像CMS 收集器 能同时和应用线程 一起并发的执行;
实现压缩空间时用更少的停顿时间;
满足可预测的GC停顿时间需求;
不要牺牲太多的吞吐性能;
不需要占用更多的Java Heap;
未来 G1 计划要全面取代CMS的。G1相比CMS有更多的优势,G1是压缩型收集器,G1通过依赖regions分区,可以实现压缩更充分。这样消除大部分潜在的碎片问题。G1提供更精准的可预测的垃圾停顿时间,满足用户指定垃圾回收时间的需求。
分为三个固定内存大小的部分:、young区,old区,permanent区。

5.1 G1的几个概念
- Region 不用
- Snapshot-The-Beginning(SATB):它是通过Root Tracting得到的,GC开始时候存活对象的快照。
- Rset:它的意思就是region B的一个card里有引用指向region A。所以对region A来说,该RSet记录的是points-into的关系;而card table仍然记录了points-out的关系。
5.2 YoungGC
- 新对象进入Eden区
- 存活对象拷贝到Surivior区
- 存活时间达到年龄后,对象就去到Old区
5.3 MixedGC
- 不是FullGC,回收所有Young和部分Old
- global concurrent marking
5.4 global concurrent marking
- lnitial marking phase:标记GC ROOt,STW
- Root region scanning phase:标记存活Region
- Concurrent marking phase:暴击存活对象
- Remark phase:重新标记,STW
- Cleanup phase:部分STW
5.5 MixedGC 时机
- lnitiatingHeapOccupancyPercent:堆占有率达到这个数值则触发global concurrent marking,默认45%
- G1HeapWastePercent:在globalxxxxx结束之后,可以知道有多少空间被回收,在每次YGC之后和再次发生Mixed GC之前,会检查安垃圾占比会否达到此参数,只要达到了,下次才会发生Mixed GC。
5.6 MixedGC 相关参数
- G1MixedGCLiveThresholdPercent:Old区的region被回收时候的存活对象占比(百分之八十)
- G1MixedGCCountTarget:一次global conxxx之后,最多执行MixedGC的次数
- G1OldCsetRegionThresholdPercent:一次MixedGC中能被选入CSet的最多old区Region数量
5.6 常用参数
-XX:+UseG1GC //开启G1
-XX:G1HeapRegionSize=n,//region的大小,1-32M,2048个
-XX:MaxGCPauseMillis=200 //最大停顿时间
-XX:ParallelGCThreads=n //SWT线程数
-XX:ConcGCThreads=n //并发线程数量=1/4*并行
5.7 最佳实践
- 年轻代大小: 避免使用-Xmn、-XX:NewRatio等显示设置Young区大小,会覆盖暂停时间目标。
- 暂停时间目标:暂停时间不要太严苛,其吞吐量目标是90%的应用程序时间和10% 垃圾回收时间,太严苛会直接影响到吞吐量
5.8 关于MixGC调优
-XX:lnitiatingHeapOccupancyPercent
-XX:G1MixedGCLiveThresholdPercent
-XX:G1HeapWastePercent
-XX:G1MixedGCCountTarget
-XX:G1OldCSetRegionThresholdPercent
5.9 关于是否需要切换到G1
- 50%以上的堆被存活对象占用
- 对象分配和晋升的速度变化非常大
- 垃圾回收时间特别长,超过了1秒
对于本次章节是对JVM入门之内存结构与调优介绍记录和学习。