JVM内存模型和GC

本文详细介绍了JVM的工作原理,包括内存结构(堆、方法区、栈、本地方法栈和程序计数器),以及垃圾回收的分代策略、内存分代(新生代、老年代)和不同阶段的GC逻辑。还探讨了常见的垃圾回收器和算法,如Serial、ParNew、ParallelScavenge等。
摘要由CSDN通过智能技术生成

什么是JVM?

JVM是可运行JAVA代码的虚拟计算机,包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收,堆和一个存储方法域。
运行过程:JAVA源文件——>编译器——>字节码文件(Class文件)——>JVM——>机器码

一.JVM内存模型

在这里插入图片描述

:JVM内存管理最大的一块,被线程共享,目的是存放对象的实例,几乎所有的对象实例都会放在这里,当堆没有可用空间时,会抛出OOM异常.根据对象的存活周期不同,JVM把对象进行分代管理,由垃圾回收器进行垃圾的回收管理
方法区:又称非堆区,用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器优化后的代码等数据
:又称方法栈,线程私有的,线程执行方法是都会创建一个栈阵,用来存储局部变量表,操作栈,动态链接,方法出口等信息.调用方法时执行入栈,方法返回式执行出栈
本地方法栈:与栈类似,也是用来保存执行方法的信息.执行Java方法是使用栈,执行Native方法时使用本地方法栈.
程序计数器:保存着当前线程执行的字节码位置,每个线程工作时都有独立的计数器,只为执行Java方法服务,执行Native方法时,程序计数器为空.

二.垃圾回收GC

GC采用了分代策略来进行垃圾回收,原因有以下几种:

  1. 不同对象的生命周期:在程序运行过程中,有些对象是短暂存在的,而有些对象可能会存活很长时间。分代GC策略基于“弱代假说”,即大多数对象会很快变得无用,而少数长寿命对象会存活下来。通过将内存划分为新生代和老年代,GC可以对这两种对象进行差异化处理,从而提高效率。
  2. 优化内存管理效率:分代GC策略允许JVM针对不同的内存区域使用不同的GC算法。例如,在新生代中,对象的回收频率较高,所以通常使用快速的GC算法;而在老年代,对象的回收频率较低,因此可以使用更复杂、但影响范围较小的GC算法。这种策略可以根据各代的特性来优化内存管理效率。
  3. 降低GC暂停时间:长时间的GC暂停可能会对程序性能造成严重影响。通过分代,JVM可以更频繁地、但持续时间更短地执行新生代的Minor GC,而少数时候执行影响范围更大的老年代的Major GC。这有助于降低GC的暂停时间,提高程序的响应速度。
  4. 更精细的资源分配:不同的应用程序可能对内存的使用模式有不同的需求。分代机制允许开发者更精细地控制内存的分配和回收,例如通过JVM参数调整新生代和老年代的大小或调整晋升老年代的阈值,从而更好地适应不同应用的需求。
  5. 适应不同应用的需求:随着Java技术的发展,其在服务器、桌面、移动设备等多种环境中都有广泛应用。这些应用对GC性能的需求各不相同。分代GC策略为JVM提供了足够的灵活性,使其能够根据不同应用环境的特点进行调优。

1.分代回收

GC根据对象的特点对内存做了内存分代,JDK8以前主要包括新生代、老年代和永久代。JDK8之后主要是新生代和老年代。
新的对象总是会被分配到新生代中,新生代空间满了之后,执行一次GC(minor GC),同时提升幸存对象的年龄属性。达到一定的阈值之后,对象会提升到老年代中,老年代空间满了之后,也会执行一次GC (major GC)。这是分代回收的一个大概流程,不过在新生代中并不是只有空间满了之后才会执行GC回收操作。
在这里插入图片描述
如上图所示,新生代分为三块:Eden(伊甸)区S0区S1区。看见伊甸区这个名字应该会有点预感吧,新生代的初始新对象就是分配到Eden区,Eden空间满了之后执行一次GC动作。幸存对象移动到S0区,对象年龄标识+1。下一次GC动作执行后,Eden区和S0区的幸存对象会移动到S1区,对象年龄标识+1。再之后执行GC,就是S1——>S0——>S1这么个流程。这个过程中幸存对象的年龄标识达到一定的阈值就会如之前说的,提升到老年代空间中去。

JVM内存新生代Eden区和Survivor区的比例是8:1:1,其中,Eden区占用80%,Survivor占用20%,并且划分为大小相同的两部分,这样划分的原因是为了解决内存碎片的问题。

2.minor GC相关逻辑

执行15次GC后对象进入老年代:这个15是默认配置,可以通过JVM参数 -XX:MaxTenuringThreshold来设置
动态年龄判断:一批对象的总大小大于这块Sunvivor区域内存大小的50%(由-XX:TargetSurvivorRatio参数指定),那么此时大于等于这批对象年龄最大值的对象,就可以直接进入老年代了。
这里这个年龄不是上面15次这个数字。举个例子,现在S0区年龄1、2、3、、、n的对象合计占了当前区域的50%,那么大于等于n的对象就直接进入老年代。
大对象直接进入老年代:-XX:PretenureSizeThreshold 指定大于该数值的对象直接进入老年代,避免在新生代的Eden和两个Survivor区域来回复制,产生大量内存复制操作
MinorGC后对象太多无法进入Suvivor区如怎么办:部分对象直接进入老年代
老年代空间分配担保:详情如下图
在这里插入图片描述
需要注意的是,如果执行fullGC后老年代空间大小还是不够用的话,就会抛出OOM内存溢出异常了。

3.常见垃圾回收器

回收器回收区域回收算法回收器特性
Serial新生代标记-复制单线程
ParNew新生代标记-复制多线程
Parallel Scavenge新生代标记-复制多线程
Serial old老年代标记-整理单线程
Parallel old老年代标记-整理多线程
CMS老年代标记-清除并发多线程
G1划分Region,新生代/老年代整体标记-整理、局部Region标记-复制并发多线程

4.GC常见算法

引用计数器:假设有一个对象A,任何对象对A进行引用,那么对象A的引用计数器+1,当引用失效时,对象A的引用计数器-1,当对象A的引用计数器为0时,就说明对象A没用被引用,那么就可以进行回收。
标记-复制:将内存空间分为两份,每次只使用其中一份。垃圾回收时将存活对象复制到另一个空间,然后清除该空间,交换两个内存角色完成垃圾回收。新生代的垃圾回收就是用这种方式。
标记-清除:分为两个阶段。第一阶段标记,从根节点开始标记引用的对象;第二阶段清除,未标记的对象就是垃圾对象,可以清除。
标记-整理:同样分为两个极端。第一阶段标记时一样的;第二阶段开始清理前先将标记对象压缩到内存的一端,然后清理边界以外的对象。这种方式可以有效减少内存碎片化的问题。当然,多了一步移动对象的动作,对效率有一定的影响。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值