JVM内存模型及垃圾收集器介绍

一、JVM的内存模型

在这里插入图片描述
Metaspace:Class、Package、Method、Field、字节码、常量池、符号引用等
CCS:32位指针的Class,没开启短指针压缩的话不会有这个。-XX:+UseCompressedClassPointers
CodeCache:JIT编译后的本地代码,JNI使用的C代码。开启编译执行才有。-Xint=解释执行。

运行时数据区

程序计数器PC Register
JVM支持多线程同时执行,每一个线程都有自己的程序计数器,线程正在执行的方法叫做当前方法,如果是Java代码,程序计数器里存放的就是当前正在执行的指令的地址。

虚拟机栈JVM Stacks
Java虚拟机栈是线程私有的,它的生命周期与线程相同。虚拟机栈描述的事Java方法执行的内存模型,每个方法在执行的同时会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

堆Heap
Java堆是Java虚拟机管理的内存中最大的一块。堆是被所有线程共享的一块区域,在虚拟机启动时创建。此内存区域的唯一目的是存放对象实例,几乎所有的对象实例都在这里分配内存、Java堆可以处理物理上不连续的内存空间。只要逻辑上是连续的即可。

方法区Method Area

方法区与Java堆一样。是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它有一个别名Non-Heap(非堆),与Java堆区分开来。
运行时常量池 RunTime Constant Pool。是方法区的一部分。除了有Class文件中类的版本、字段、方法、接口等描述信息外。还有一项信息是常量池( Constant Pool),用于存放编译期生成的各种字面量和符号引用,这部分内容在类加载后进入方法区的运行时常量池中存放。

本地方法栈

本地方法栈与虚拟机栈所发挥的作用是类似的。他们之间区别不过是虚拟机栈作为虚拟机执行Java方法服务,而本地方法栈则为虚拟机使用到Natice方法服务。


二、垃圾回收算法

Java采用枚举根节点,做可达性分析来进行垃圾回收。
根节点:类加载器,Thread、虚拟机栈的本地变量表、static成员、常量引用、本地方法栈的变量等待

1.标记清除算法
首先标记出所有需要回收的对象,在标记完成后统一回收所有对象。
缺点:效率不高。算法效率低。产生内存碎片,碎片太多会提前导致GC。

2.复制算法 把内存划分为大小相等的2块,每次只使用一块。当一块内存用完了,把存活对象拷贝到另一块上面。再把这一块一次性全部清除。 缺点:空间利用率低。

3.标记整理 类似标记清除,不过清理步骤不同。让所有存活对象向一端移动,然后直接清理掉端边界以外的内存。 缺点:整理内存耗时。

Java采用分代垃圾回收(JDK11的ZGC就不是分代回收了)
Young区采用复制算法,因为Young区的对象生命周期大多比较短。
Old区采用标记整理或标记清除,Old区的对象大多生命周期大多比较长。如果采用复制算法,复制来复制去都存在,效率不高。

Java中对象内存分配过程
优先在Eden区分配
大对象直接进入老年代,定义大对象的大小参数:-XX:PretenureSizeThreshold
长期存活对象进入老年代。多次在YGC存活下来的对象。发生一次,这个对象的年龄+1。定义次数:-XX:MaxTenuringThreshold,垃圾回收之后Survivor大小比例达到这个值-XX:TargetSurvivorRatio,就计算对象的平均年龄是否超过-XX:MaxTenuringThreshold。超过的话也会转移到Old区。-XX:PrintTenuringDistribution发生YGC时打印对象的年龄情况


三、垃圾收集器

串行的Serial(单个垃圾回收线程):Serial、Serial Old
并行的Parallel(多个垃圾回收线程):Parallel Scavenge、Parallel Old,吞吐量
并发的(多个垃圾回收线程):CMS,G1,停顿时间

并行:多个垃圾收集器线程并行工作,但此时用户线程仍处于等待状态。适合科学计算,后台处理等弱交互场景。
并发:指在执行垃圾回收线程的时候可以同时执行用户线程。不影响用户线程的运行。适合Web交互应用。
停顿时间:垃圾收集器在做垃圾回收的时候中断应用执行的时间,-XX:MaxGCPauseMillis
吞吐量:花在垃圾收集的时间和花在应用时间的占比。-XX:GCTimeRatio=,垃圾收集时间占:1/1+n

串行收集器,不适用
-XX:+UseSerialGC -XX:+UseSerialOldGC
并行收集器,吞吐量优先,默认关闭的。可以动态调整内存
-XX:+UseParallelGC  -XX:+UseParallelOldGC 
-XX:ParallelGCThreads=<N> 开启N个GC线程,CPU>8核,默认开启5/8,CPU<8,默认N=CPU
-XX:MaxGCPauseMillis=<N> 最大GC停顿时间
-XX:GCTimeRatio=<N> 吞吐量
-Xmx<N> 最大堆大小
并行收集器会自动根据这些参数调整堆的大小,优先满足停顿时间的要求,吞吐量的要求
JVM的动态内存调整的参数
-XX:YoungGenerationSizeIncrement=<Y> Young区默认自动调大比率,默认20%
-XX:TenuredGenerationSizeIncrement=<Y> Old区默认自动调大比率,默认20%
-XX:AdaptiveSizeDecrementScaleFactor=<Y> 默认自动调小的比率,默认4%
JVM可以通过这些参数来动态调整堆的大小
并发收集器,响应时间优先
并发收集,可以与应用线程同时执行。低停顿,低延迟。

---
CMS:老年代收集器。开启:-XX:+UseConcMarkSweepGC  -XX:+UseParNewGC 
回收过程:
CMS initial Mark:初始标记GC Root,STW
CMS concurrent Mark:并发标记,从根节点往下开始标记。非STW
CMS-concurrent-preclean:并发预清理
CMS remark:重新标记,STW
CMS concurrent sweep:并发清除
CMS-concurrent-reset:并发重置
缺点:
CPU敏感
浮动垃圾,在GC过程中应用程序线程还在运行,可能会产生垃圾,产生空间碎片
相关参数:
-XX:ConcGCThreads:并发GC线程数
-XX:+UseCMSCompactAtFullCollection:FullGC之后做内存压缩
-XX:CMSFullGCsBeforeCompaction:多少次FullGC之后压缩一次  最好是1
-XX:CMSInitiatingOccupancyFraction:Old区占满多少才会触发FullGC,默认92%
计算公式 <=((Xmx-Xmn)-(Xmn-Xmn/(SurvivorRatior+2)))/(Xmx-Xmn)*100   53
-XX:+UseCMSInitiatingOccupancyOnly:上面这个参数时候动态可调
-XX:+CMSScavengeBeforeRemark:FullGC之前先做YGC

---
G1:年轻代老年代都可以用的收集器。开启:-XX:+UseG1GC
SATB:通过Root Tracing得到的,GC开始的时候存活对象的快照。
RSet:记录了其他Region中的对象引用本Region中对象的关系。标记谁引用了我的对象。
 
G1里没有FullGC概念。叫MixedGC。回收所有的Young和部分的Old。 
回收过程: 
global concurrent marking:全局并发标记
Initial marking phase:标记GC Root,STW
Root region scanning phase:标记存活Region
Concurrent marking phase:标记存活对象
Remark phase:重新标记,STW
Cleanup phase:部分STW

MixedGC时机:InitiatingHeapOccupancyPercent:堆占有率达到这个比率触发global concurrent marking。默认45%
在global concurrent marking结束之后,可以知道内存区有多少空间要被回收,在每次YGC之后和再次发生MixedGC之前,
会检查垃圾占比是否达到G1HeapWastePersent,只有达到了,下次才会发生MixedGC。

相关参数:
G1MixedGCLiveThresholdPersent:Old区的Region被回收的时候的存活对象占比
G1MixedGCCountTarget:一次global concurrent marking之后,最多执行MixedGC的次数
G1OldCSetRegionThresholdPersent:一次MixedGC最多回收多少Old区的Region数量
-XX:G1HeapRegionSize=<N> Region的大小,1-32M,最多2048个
-XX:MaxGCPauseMillis=<N> 最大GC停顿时间
-XX:G1NewSizePersent -XX:G1MaxNewSizePersent:Young区的堆大小比率
-XX:G1ReservePersent=10:保留,防止YGC拷贝对象时内存不够
-XX:ParallelGCThreads=n:停止应用程序,STW时的线程数。执行标记,清除的时候的线程
-XX:ConcGCThreads=n:并发线程数=1/4*并行

最佳实践:
年轻代大小:避免使用-Xmn、-XX:NewRatio等显示设置Young区大小,会覆盖暂停实践目标暂停实践目标:
暂停时间不要太严苛,其吞吐量目标是90%的应用程序时间和10%的垃圾回收时间,太严苛会影响到吞吐量

G1把内存分为多个Region,不超过2048个。Young和Old是逻辑上的概念。H用来存放大对象。
在这里插入图片描述

垃圾收集器的搭配使用
在这里插入图片描述

如何选择垃圾收集器

优先调整堆的大小让服务器自己来选择
如果内存小于100M,使用串行的收集器
如果是单核CPU,并且没有停顿时间的要求,串行或者JVM自己选择
如果允许停顿时间超过1秒,选择并行或者JVM自己选择
如果响应时间最重要,并且不能超过1秒,使用并发收集器


在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值