Java必突-JVM知识专题(二):Java堆内存+JVM的垃圾回收机制是用来干什么的+JVM分代模型:年轻代、老年代、永久代、方法区内会不会进行垃圾回收呢?+JVM内存相关的几个核心参数图解

前言:

1.JVM的垃圾回收机制是用来干什么的?
2.为什么要垃圾回收?
3.创建的这些对象,到底在Java堆内存中会占用多少内存空间呢?
4.JVM分代模型:年轻代、老年代、永久代、方法区内会不会进行垃圾回收呢?
5. 对象到底什么时候进入新生代?
6.什么情况下会进入老年代?
7. JVM内存相关的几个核心参数图解、如在启动系统的时候设置JVM参数呢?
8.如果是线上部署系统应该如何设置JVM参数呢?
9.自己负责的线上系统,到底如何合理设置JVM内存大小?

1.JVM的垃圾回收机制是用来干什么的?为什么要垃圾回收?

来个demo:

先举个例子吧,不然理解对于刚刚接触jvm偏底层的有些不大友好:酒店开完房,睡完觉,第二天退房,酒店是否要进行房间打扫,说白了粗鲁点:打扫房间就是垃圾回收,当然只是为了更好的继续向下理解,概念差不多,说到这里,也简单的做个铺垫:Stop the world!不需要知道是干嘛的,有什么用,就想成,阿姨正在打扫房间的时候,酒店现在来客人了,酒店一定不会在还没打扫完的房间给客人,只是铺垫,方便理解;

1.1:进入正题回顾下:【分重点标注】

创建的Java对象其实都是占用内存资源的,例如在执行main()方法的时候,new User();实例化了User对象,那么User对象的变量就会存入虚拟机栈的栈帧中,还会在Java堆内存中创建一个User()对象,虚拟机栈的栈帧中的user变量是引用的Java堆内存中的地址值。

2:为什么要垃圾回收【无用的东西,还留着干啥?】

此时User()已经执行完成了,Java虚拟机栈弹栈了,也就是此时在Java堆内存中的实例对象没有方法引用了,也就是说没有一个变量是指向Java堆内存中的User()对象了。创建的Java对象其实都是占用资源的,User()对象该干的事已经干完了,还留在内存干啥呢?【Java堆内存中创建的对象,都是占用内存资源的,而且内存资源是有限的】引入Java堆内存了:不在需要的对象该怎么处理呢?垃圾回收,叫保洁阿姨打扫卫生=
在这里插入图片描述

简单小结下:

如果某个实例对象没有如何一个方法的局部变量指向它,也没有任何一个类的静态变量,包括常量等地方指向它,那么这个垃圾回收线程就会把这个没人指向的User实例对象给回收掉,从内存里清除掉,让他不再占用任何的内存资源;这样的话这些没有被指向的对象实例,就是JVM中的**‘垃圾’**,就会定期的被后台垃圾回收线程清理掉,不断释放内存资源;

3.创建的这些对象,到底在Java堆内存中会占用多少内存空间呢?【铺垫下,后会有期】

3.1:了解:一个对象对内存空间的暂用,大致分为两块:

1.一个是对象本身的一些信息;
2.一个是对象的实例变量作为数据占用的空间

3.2:比如对象头:

如果是64位的Linux操作系统上,会占用16字节,如果实例对象的内部有个int类型的实例变量,会占用4个字节,如果是long的实例变量会暂用8个字节,如果是数组、map之类的,就会占用更多的内存了。依次类推;

4.JVM分代模型:年轻代、老年代、永久代、方法区内会不会进行垃圾回收呢?

4.1.年轻代和老年代主要作为重点关注研究:

写代码的方式不同,采用不同的方式来创建和使用对象,对象的生命周期是不同的,所以JVM将Java堆内存主要大方向的划分为了两个区域,一个是年轻代,一个是老年代

4.2.先聊聊年轻代:

年轻代就是创建和使用完之后立马就要回收的对象放在年轻代中;重点多看两遍

4.3:再简单聊聊老年代:

老年代就是创建之后需要一直长期存在的对象放在里面;两者截然不同

于是堆内存就简化变成了这样【如图】
Java堆内存1.0
4.4:简单代码示例一下年轻代和老年代

public class Study {
    //TODO 老年代
    public static User user = new User();
    
    //TODO 程序主入口
    public static void (String[] args) throws InterruptedException {
        loadMe();
        while (true){
            oneLoad();
            Thread.sleep(3000);
        }
    }
    private static void loadMe(){
        //TODO 年轻代
        Classes cla = new Classes();
        cla.getClassesNum();
    }
    private static  void oneLoad(){
        user.getAge();
    }
}

解释下:

1.main的静态变量user引用了User();对象,这是长期需要驻留在内存中使用的,这个对象会在年轻代里停留一会儿,但是最终回进入老年代;

2.进入main方法之后,会先调用loadMe()方法,这个方法的栈帧进栈,方法中创建了一个Classes()对象,这个对象用完就会被垃圾回收,由栈帧中的局部变量来引用,一旦loadMe()方法执行完毕了,方法的栈帧就会出栈,对应的年轻代中的Classes()对象就会被回收;

3.接着执行一段while循环代码,周期性的调用User的getAge();去加载副本数据,所以User()这个对象因为被Study类的静态变量user引用了,所以会长期存在老年代中,持续的被使用。

在这里插入图片描述
loadMe()执行完毕,弹栈回收【也就变成如下图】
弹栈回收
4.5:那什么是永久代呢?

【JVM的永久代其实就是方法区,方法区就是所谓的永久代,永久代就是放一些类信息的】
在这里插入图片描述

4.6:永久代,就一定不会被回收吗?NO!
满足以下三种情况,方法区中的类就会被回收:

1.该类的所有实例对象都从Java堆内存中被回收了;
2.加载这个类的ClassLoader类加载器已经被回收了;
3.该类的Class对象没有任何引用啦;
满足就进行:垃圾回收

5.对象到底什么时候进入新生代?什么情况下会进入老年代?

5.1.前言:

大部分正常的对象都是优先在新生代分配内存的【这个要走心,伏笔,为什么是大部分】

5.2.代码回顾:

public class Study {
    //TODO 老年代
    public static User user = new User();
    
    //TODO 程序主入口
    public static void (String[] args) throws InterruptedException {
        loadMe();
        while (true){
            oneLoad();
            Thread.sleep(3000);
        }
    }
    private static void loadMe(){
        //TODO 年轻代
        Classes cla = new Classes();
        cla.getClassesNum();
    }
    private static  void oneLoad(){
        user.getAge();
    }
}

静态变量user引用的User对象,是会长期存活在内存中的,这种对象,其实刚刚开始通过new
User()代码来实例化一个对象时,它也是分配在新生代中的,包括在loadMe()方法中创建的Classes实例对象,也都是一样分配在新生代中的;这样标注没毛病吧。
【贴图示意:注意配合图中的线条颜色】
在这里插入图片描述

5.3:说了这么多?吊我胃口?到底什么时候会触发新生代的垃圾回收机制呢?

假设,一旦loadMe()方法执行完毕后,这个方法的栈帧就会出栈,会导致没有任何局部变量引用Classes实例对象了。loadMe执行完毕,出栈:黑红色,弹栈完毕后就一定会发生垃圾回收,去回收掉Java堆内存中没有人使用的Classes实例对象吗?NO!

5.4:垃圾回收触发的一些基础知识机制【认真看完,没问题】

垃圾回收他也得又触发垃圾回收机制的条件,一个比较常见的场景可能是这样的,假设代码中创建了N多个对象,然后导致Java堆内存中囤积了大量的对象,然后这些对象都是之前有人引用的,例如各种各样的方法中的局部变量,但是现在也都没人引用了。这个时候,如果新生代我们预先分配的内存空间,几乎都被全部对象给占满了,此时假设代码继续运行,他需要在新生代中去分配一个对象,发现新生代中的内存空间都不够了,这个时候就会触发一次新生代内存空间的垃圾回收,新生代内存空间的垃圾回收,也称为“Minor GC”,有时候也叫"Young GC",他会尝试把新生代中那些没有人引用的垃圾对象都给回收掉,例如图中的Classes实例对象,其实就是没有人引用的垃圾对象,此时就会即可,将Classes实例对象回收掉,腾出更多的内存空间,然后放一个新的对象到新生代中去,包括图中的大量的实例对象,其实也都没有人引用,这这个新生代垃圾回收的过程中,就会将这些垃圾对象都回收掉。

5.5:实际场景出发考虑呢?

实际在代码中创建的大部分对象,其实都是使用之后立马就可以回收掉的生存周期极短的对象?可能会在新生代中分配大量的对象,但是使用完事后立马就没人引用了,此时新生代差不多满了,需要分配新的对象的时候,发现新生代内存不足,就会触发一次垃圾回收,然后把所有垃圾对象干掉,腾出大量的内存空间

5.6:长期被minor gc垃圾回收没被回收掉的钉子户咋办?

图中User对象,长期被Study类的静态变量user变量引用长期存活的对象,所以虽然新生代可能随着系统的运行,不停的创建对象,然后让新生代变满,接着垃圾回收一次,大量的对象被回收掉了,但是这个User对象的确是一直存活在新生代中,因为被Study类的静态变量给引用了,所以User实例对象不会被回收,那么

《JVM就有一条规定了》:老实,瑟瑟发抖: 如果一个实例对象在新生代中,成功的在15次垃圾回收之后,还是没有被回收掉,说明这个对象已经15岁了,这是对象的年龄,每垃圾回收躲过一次,年龄+1,那么如果User对象成功在新生代中成功躲过10次垃圾回收,成为一个“老年人”,那么就被认为是会长期活在内存中的对象,就会被转移到Java堆内存的老年代中去,老年代就是放的年龄很大的对象
进入老年代

【这里也简单做个铺垫】

其实不关只是这种年龄到达一定的年龄就被进入老年人了,还有很多种规则都会进入老年代,例如:
1.新生代垃圾回收之后,因为存活的对象太多,导致大量的对象直接进入老年代;
2.特别大的超大对象直接不经过新生代就直接进入老年代;
3.动态对象的年龄判断机制
3.空间担保机制等等

5.7:老年代里的那些对象会被垃圾回收吗?

答案会肯定的,因为老年代的对象也有可能随着代码的运行,不再被任何人引用了,就需要被垃圾回收;【Full GC】至于什么时候触发,慢慢来,循循渐进;

6:JVM内存相关的几个核心参数图解【有点丑,但是不影响我装逼】

有点丑,但是不影响我装逼

7.那么说了这么多,如何设置JVM参数呢?

7.1:【图已画好,经供参考,IDEA】
在这里插入图片描述
7.2:springboot设置jvm参数:

启动的时候可以添加JVM参数

7.3:如果是线上部署系统应该如何设置JVM参数呢?

比如采用"java -jar"的方式启动一个jar包中的系统,那么就可以采用类似下面的这种格式启动:

java -Xms512M -Xmx512M -Xmn256M -Xss1M -XX:PermSize=128M -XX:MaxPermSize=128M -jar App.jar

7.4:tomcat服务器如何设置JVM参数?

Tomcat就是在bin目录下的catalina.sh中可以设置JVM参数的

8.自己负责的线上系统,到底如何合理设置JVM内存大小?

现在还无法根据jvm一和二合理判断,越往后看越知道,彩蛋总会留在最后,谢谢大家!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咖喱ABC

无需打赏,共同进步学习!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值