让人又爱又恨的JVM

1 篇文章 0 订阅

JVM内存

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-327iEtx8-1634447701538)(.\图片\JVM02.png)]

1.方法区 – 存放字节码文件

2.程序计数器-- 记录字节码文件执行到那个位置

3.Java虚拟机栈 – 程序方法压栈出栈 方法中的局部变量

4.Java堆内存 – 方法执行中创建的对象存储

5.常量池 – static修饰的一般常量存储空间 图示未体现

6.内存其他区域 本地方法栈 native修饰的一些变量

新生代老年代

在这里插入图片描述

新生代:创建和使用完后的对象立马放的区域 注意:刚开始创建的对象大部分优先分配在新生代;什么时间变为老年代?

老年代:创建后需要长期使用的对象

新生代转老年代:默认新生代对象15次垃圾回收还未被回收掉的对象会转到老年代
新生代触发垃圾回收条件:新生代内存满了,创建新的对象时触发  YOUNG GC
注意:新生代触发垃圾回收后还有很多对象,新创建的对象可能会直接进入老年代内存;
     特大超大的对象直接进入老年代
老年代触发垃圾回收条件:老年代内存满了,触发垃圾回收

永久代:其实就是方法区

为什么区分:

垃圾回收算法有关,新生代的垃圾回收算法和老年代的垃圾回收算法不同;

JVM内存分配相关参数

为什么了解参数:频繁的GC容易造成线上问题,有需要的话我们要结合业务和估算来进行JVM调优
在这里插入图片描述

相应的springboot上面查找

启动jar文件指令:jar -Xms512M -Xmx512M … -jar App.jar

调优前需要考虑
  • 堆内存估算方式:每个对象占的字节,每秒多少对象?多久一次GC合适?选择内存多大的服务器?多少台服务器?
  • 方法区内存设置:一般上线时设置几百MB是够用的,但是要观察后续的运行情况
  • 栈内存的设置:一般默认512kb到1MB

JVM类加载

在这里插入图片描述

1.加载时机 —用到才加载

2.双亲委派模型 防止类重复加载

3.验证 准备 解析 初始化都做了那些事情

3.1 验证 检验字节码文件符合虚拟机要求

3.2 准备 为加载的类分配内存空间 静态变量分配空间并给出一个初始值;

3.3 解析 符号引用替换为直接引用

3.4 初始化 变量赋值 静态代码块执行 子类初始化是父类必先初始化

JVM垃圾回收机制

在这里插入图片描述

1.回收机制 实例对象没有任何一个方法局部变量、局部变量、常量指向它,一般会被认为垃圾;

思考:方法去中的类会被垃圾回收吗?什么时间回收?

会。
满足三种情况下会回收:
1.该类在堆的所有实例对象君被回收;
2.加载该类的ClassLoader被回收
3.该类的Class对象没有任何引用
回收时机

1.被局部变量、静态变量直接强绑定的对象在垃圾回收时是不会被回收的,被类间接引用的软引用在内存满了的情况下是会被回收的;

软引用示例:

// student 相当于弱引用
    private static People people = new People(new Student());
回收算法
复制算法

在这里插入图片描述

创建的对象首先存在上方内存区域,触发GC后,把还在使用的对象复制到下方的内存区域,上方剩下的垃圾对象整体回收。

好处:防止一个区域造成的大量内存碎片化情况

缺点:需要一般的长空内存区域,内存利用率不高

复制算法优化 Eden区和Survivor区

在这里插入图片描述

1.创建对象首次放在Eden区,第一次触发GC后,还在使用的对象放在第一个Survivor区,清空Eden区的垃圾对象;

2.第二次触发GC后,Eden区和放Survivor的使用对象复制到为空的另一个Survivor区域,清空Eden和Survivor内的垃圾对象;

3.上述第二步循环;

好处:内存利用率提升。

思考:一些万一情况

1.垃圾回收后,survivor放不下?

2.超大对象,survivor放不下?

老年代相关

在这里插入图片描述

新生代进入老年代的情况

1.多次young GC后,依然存活的对象(默认15次)

2.动态年龄判断–survivor区域存活对象大于50%了,此时触发young GC后大于平均年龄也会直接进入老年代;

3.大对象直接进入老年代(JVM参数可以设置参考大小值)

4.young GC后存活对象超过Survivor区域大小,直接进入老年代;

老年代内存分配规则

1.在执行young GC之前都会先检查老年代的内存大小,是否能存放所有的新生代对象;如果大于老年代内存空间,没设置平均新生代每次像老年代同步对象大小时,会直接触发FULL GC(对老年代垃圾对象进行回收)

注意:FULL GC算法比young GC算法慢十倍,会造成系统卡顿等情况;

老年代回收算法

标记算法:标记那些对象是垃圾对象,然后把这些对象清理掉;还要防止内存碎片化(可通过参数设置决定几次FULL GC后压缩内存,降低内存碎片化的影响),算法复杂;

所以所谓的JVM优化,就是尽量让对象在新生代回收,避免频繁的进行老年代垃圾回收,同时也要避免新生代频繁的垃圾回收;

JVM垃圾回收器

serial&serial old垃圾回收器

分别用来回收新生代和老年代的垃圾对象

单线程模式,开启后会让运行系统卡死,现在Java后台几乎不用;

ParNew &CMS垃圾回收器

ParNew:一般新生代垃圾对象回收

  • 垃圾回收时,系统程序工作的所有线程均停掉,多线程开启回收

CMS:老年代垃圾回收

CMS四步:

  • 初始标记 系统工作线程全部停止,速度很快
  • 并发标记
  • 重新标记 系统工作线程全部停止,速度很快
  • 并发清理

多线程垃圾回收,性能较好,一般生产常用

CMS垃圾回收器采用的是垃圾回收线程和系统线程尽量同时进行,防止很长时间的Stop the World

G1垃圾回收器

统一收集新生代和老年代,性能更好;

特点:把内存拆分为很多小的region,新生代和老年代各自对应一些region,回收的时候尽量以最小的时间回收垃圾对象最多的region,尽量保证我们指定的垃圾回收时系统的停顿时间;

最大特点:可以设置一个垃圾回收最大的预期停顿时间;

一般内存比较小的机器使用parNew垃圾回收新生代一般没有问题,一次就几十毫秒停顿,但是当机器内存很大,比如32核64G的,堆内存可能时32G,而新生代区域7G,可以想一下每次垃圾回收可能系统的停顿时间就上去了,此时用G1垃圾回收,设置系统停顿时间往往是最好的;

Stop the World

垃圾回收时,系统要暂停不能创建新对象,这就是Stop the World;

不同的是:单线程垃圾回收还是多线程垃圾回收,垃圾回收的频率和时间;

在这里插入图片描述

万恶的OOM

会造成OOM的区域
1.永久代内存溢出(存放方法区的区域);

jvm参数设置示例:

-XX:MetaspaceSize=512m

-XX:MaxMetaspaceSize=512m

上述参数是给永久代分配512M的存储区域;

什么情况下会造成永久代区域的内存溢出:

1.jvm参数设置采用的默认设置,默认设置永久代一般只分配几十M的大小,系统稍微大点就会触发oom

2.cglib动态生成类写的有点问题,大量的动态类创造总成;

2.每个线程的虚拟机栈内存溢出;

线程一般默认的是1m

栈内存一般是方法的出栈和入栈,如果方法深度比较大,每个方法中的局部变量比较多可能会造成这个情况

最常见的原因:递归使用不小心;

一般不会出现;

3.堆内存空间溢出;

触发条件:有限的堆内存中存放了过多的存活对象造成;

一般两种场景下会出现:

1.系统高并发,请求量过大,导致存活对象太多,继续放入新对象时放不下了崩溃;

2.一些对象的引用没有及时取消;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值