JVM实战(二):JVM分代模型:年轻代、老年代、永久代

目录

1、JVM分代模型:年轻代、老年代、永久代

1、代码示例

2、大部分对象都是存活周期极短的

3、少数对象是长期存活的

4、JVM分代模型:年轻代和老年代

5、为什么要分成年轻代和老年代?

6、什么是永久代?

7、方法区内会不会进行垃圾回收

2、你的对象在JVM内存中如何分配?如何流转的

3、跟JVM内存相关的几个核心参数图解

4、如何在启动系统的时候设置JVM参数?

5、每日百万交易的支付系统,如何设置JVM堆内存?

1、支付系统背景引入

2、支付的核心业务流程

3、每日百万交易的支付系统的压力在哪里?

4、支付系统每秒钟需要处理多少笔支付订单

5、每个支付订单处理要耗时多久?

6、每个支付订单大概需要多大的内存空间?

7、每秒发起的支付请求对内存的占用

8、让支付系统运行起来分析一下

9、对完整的支付系统内存占用需要进行预估

10、支付系统的JVM堆内存应该怎么设置?

11、本文总结

6、JVM设置不合理的案例


1、JVM分代模型:年轻代、老年代、永久代

1、代码示例

JVM实战(二):JVM分代模型:年轻代、老年代、永久代_第1张图片

        在main()方法里,会周期新的执行loadReplicasFromDisk()方法,加载副本数据。 首先一旦执行main()方法,那么就会把main()方法的栈帧压入main线程的Java虚拟机栈

JVM实战(二):JVM分代模型:年轻代、老年代、永久代_第2张图片

        然后每次在while循环里,调用loadReplicasFromDisk()方法,就会把loadReplicasFromDisk()方法的栈帧压入自己的Java虚拟机栈

 JVM实战(二):JVM分代模型:年轻代、老年代、永久代_第3张图片

        接着在执行loadReplicasFromDisk()方法的时候,会在Java堆内存里会创建一个ReplicaManager对象实例,而且loadReplicasFromDisk()方法的栈帧里会有“replicaManager”局部变量去引用Java堆内存里的ReplicaManager对象实例

JVM实战(二):JVM分代模型:年轻代、老年代、永久代_第4张图片

然后就会执行ReplicaManager对象的load()方法。

2、大部分对象都是存活周期极短的

         ReplicaManager对象属于短暂存活的这么一个对象 ,在loadReplicasFromDisk()方法中创建这个对象,然后执行ReplicaM anager对象的load()方法, 执行完之后,loadReplicasFromDisk()方法就会结束。一旦方法结束,那么loadReplicasFromDis k()方法的栈帧就会出栈: JVM实战(二):JVM分代模型:年轻代、老年代、永久代_第5张图片

一旦没人引用ReplicaManager对象了,就会被JVM的垃圾回收线程给回收掉,释放内存空间。 JVM实战(二):JVM分代模型:年轻代、老年代、永久代_第6张图片

        然后在main()方法的while循环里,下一次循环再次执行loadReplicasFromDisk()方法的时候,又会走一遍上面那个过程,把loadReplicasFromDisk()方法的栈帧压入Java虚拟机栈,然后构造一个ReplicaManager实例对象放在Java堆里。

        一旦执行完ReplicaManager对象的load()方法之后,loadReplicasFromDisk()方法又会结束,再次出栈,然后垃圾回收释放掉Java堆内存里的ReplicaManager对象。
       Replic aManager对象是一个存活周期极为短暂的对象,可能每次执行loadReplicasFromDisk()方法时被创建出来,然后执行load()方法,接着可能1毫秒之后,就被垃 圾回收掉了。
        从这段代码就可以明显看出来,大部分在代码里创建的对象,其实都是 存活周期很短的。这种对象,其实在我们写的Java代码中,占到绝大部分的比例

3、少数对象是长期存活的

假如说咱们用下面的这种方式来实现同样的功能: JVM实战(二):JVM分代模型:年轻代、老年代、永久代_第7张图片

        给Kafka类定义一个静态变量,也就是“replicaManager”,这个Kafka类是在JVM的方法区里。然后让“replicaManager”引用了一个在Java堆内存里创建的ReplicaManager实例对象:JVM实战(二):JVM分代模型:年轻代、老年代、永久代_第8张图片

        接着在main()方法中,就会在一个while循环里,不停的调用ReplicaManager对象的load()方法,做成一个周期性运行的模式。 这个ReplicaManager实例对象,他是会一直被Kafka的静态变量引用的,然后会一直驻留在Java堆内存里,是不会被垃圾回收掉的。

        因为这个实例对象他需要长期 被使用,周期性的被调用load()方法,所以他成为了一个长时间存在的对象。类似这种被类的静态变量长期引用的对象,他需要长期停留在Java堆内存里,这这种对象就是生存周期很长的对象,他是轻易不会被垃圾回收的,他需要长期存在,不停的去 使用他

4、JVM分代模型:年轻代和老年代

        JVM的分代模型,年轻代和老年代。 根据写代码方式的不同,采用不同的方式来创建和使用对象,其实对象的生存周期是不同的。
所以JVM将 Java堆内存划分为了两个区域,一个是年轻代,一个是老年代
  • 年轻代,就是把第一种代码示例中的那种,创建和使用完之后立马就要回收的对象放在里面

老年代呢,就是把第二种代码示例中的那种,创建之后需要一直长期存在的对象放在里面。JVM实战(二):JVM分代模型:年轻代、老年代、永久代_第9张图片

我们再次来改造一下,再结合图,大家会看的更加的明确一些。

         Kafka的静态变量“fetcher”引用了ReplicaFetcher对象,这是长期需要驻留在内存里使用的 。这个对象会在年轻代里停留一会儿,但是最终会进入老年代: JVM实战(二):JVM分代模型:年轻代、老年代、永久代_第10张图片

        进入main()方法之后,会先调用loadReplicasFromDisk()方法,业务含义是系统启动就从磁盘加载一次副本数据,这个方法的栈帧会入栈。然后在这个方法里面创建了一个ReplicaManager对象,这个对象他是用完就会回收,所以是会放在年轻代里的,由栈帧里的局部变量来引用:

        然后一旦loadReplicasFromDisk()方法执行完毕了,方法的栈帧就会出栈,对应的年轻代里的ReplicaManager对象也会被回收掉:

JVM实战(二):JVM分代模型:年轻代、老年代、永久代_第11张图片

         接着会执行一段while循环代码,他会周期性的调用ReplicaFetcher的fetch()方法,去从远程加载副本数据。 所以ReplicaFetcher这个对象因为被Kafka类的静态变量fetcher给引用了,所以他会长期存在于老年代里的,持续被使用。

5、为什么要分成年轻代和老年代?

那么为什么需要这么区分呢?
  • 因为这跟垃圾回收有关,对于年轻代里的对象,他们的特点是创建之后很快就会被回收,所以需要用一种垃圾回收算法
  • 对于老年代里的对象,他们的特点是需要长期存在,所以需要另外一种垃圾回收算法,所以需要分成两个区域来放不同的对象。

6、什么是永久代?

JVM里的 永久代其实就是方法区 , 永久代就是放一些类信息的。

7、方法区内会不会进行垃圾回收

在以下几种情况下, 方法区里的 类会被回收:
  • 首先该类的所有实例对象都已经从Java堆内存里被回收
  • 其次加载这个类的ClassLoader已经被回收
  • 最后,对该类的Class对象没有任何引用
满足上面三个条件就可以回收该类了。

2、你的对象在JVM内存中如何分配?如何流转的

大部分的正常对象,都是优先在新生代分配内存的。

 什么情况下会触发新生代的垃圾回收?

        一旦“loadReplicasFromDisk()”方法执行完毕之后,这个方法的栈帧出栈,会导致没有任何局部变量引用那个“ReplicaManager”实例对象了。

此时就一定会立即发生垃圾回收,回收掉Java堆内存里没人使用的“ReplicaManager”实例对象吗?

NO! 实际上垃圾回收他也得有点触发的条件。
        假设我们写的代码中创建了N多对象,然后导致Java堆内存里囤积了大量的对象。 然后这些对象都是之前有人引用,比如各种各样的方法中的局部变量,但是现在也都没人引用了。

这个时候,如果新生代我们预先分配的内存空间,几乎都被全部对象给占满了!此时假设我们代码继续运行,他需要在新生代里去分配一个对象,怎么办?发现新生代里内存空间都不够了!

这个时候,就会触发一次新生代内存空间的垃圾回收,新生代内存空间的垃圾回收,也称之为“ Minor GC”,也叫“Young GC ”,他会尝试把新生代里那些没有人引用的垃圾对象,都给回收掉。
比如上图中,那个“ReplicaManager”实例对象,其实就是没有人引用的垃圾对象。此时就会当机立断,把“ReplicaManager”实例对象给回收掉,腾出更多的内存空间,然后放一个新的对象到新生代里去:

JVM实战(二):JVM分代模型:年轻代、老年代、永久代_第12张图片

 长期存活的对象会躲过多次垃圾回收

        “ReplicaFetcher”实例对象是一个长期被“Kafka”类的静态变量,“fetcher”引用的长期存活的对象。 所以虽然你的新生代可能随着系统的运行,不停的创建对象,然后让新生代变满,接着垃圾回收一次,大量对象被回收掉。但是你的这个“ReplicaFetcher”对象,他确是一直会存活在新生代里的。
        因为他一直被“Kafk a”类的静态变量给引用了,所以他不会被回收。那么此时JVM就有一 条规定了。 如果一个实例对象在新生代中,成功的在15次垃圾回收之后,还是没被回收掉,就说明他已经15岁了 。 这是 对象的年龄,每垃圾回收一次,如果一个对象没被回收掉,他的年龄就会增加1
所以如果上图中的那个“ReplicaFetcher”对象在新生代中成功躲过10多次垃圾回收,成为一个“老年人”,那么就会被认为是会长期存活在内存里的对象。
        然后他会被转移到Java堆内存的老年代中去,老年代就是放这些年龄很大的对象。

老年代会垃圾回收吗?

肯定的 ,因为老年代里的对象也可能随着代码的运行,不再被任何人引用了,就需要被垃圾回收。
问题:每个线程执行方法时,那些方法对应的栈帧出栈了,那么那里的局部变量需要垃圾回收吗?
J VM里垃圾回收针对的是新生代,老年代,还有方法区(永久代),不会针对方法的栈帧 。方法一旦执行完毕 ,栈帧出栈,里面的局部变量直接就从内存里清理 掉了。

3、跟JVM内存相关的几个核心参数图解

在JVM内存分配中,有几个参数是比较核心的,如下所示。
  • -Xms:Java堆内存的大小
  • -Xmx:Java堆内存的最大大小
  • -Xmn:Java堆内存中的新生代大小,扣除新生代剩下的就是老年代的内存大小了
  • -XX:PermSize:永久代大小
  • -XX:MaxPermSize:永久代最大大小
  • -Xss:每个线程的栈内存大小
          -Xms和-Xmx,分别用于设置Java堆内存的刚开始的大小,以及允许扩张到的最大大小。
对于这对参数,通常来说,都会设置为完全一样的大小。这两个参数,是用来 限定Java堆内存的总大小的。
        -Xmn,用来设置Java堆内存中的新生代的大小,然后扣除新生代大小之后的剩余内存就是给老年代的内存大小。
         -XX:PermSize和-XX:MaxPermSize,分别限定了永久代大小和永久代的最大大小。通常这两个数值也是设置为一样的,如果是JDK 1.8以后的版本,那么这俩参数被替换为了-XX:MetaspaceSize和-XX:MaxMetaspaceSize,这两个参数限定了永久代的大小。
        -Xss参数限定了每个线程的栈内存大小。每个线程都有一个自己的虚拟机栈,然后每次执行一个方法,就会将方法的栈帧压入线程的栈里,方法执行完毕,那么栈帧就会从线程的栈里出栈
JVM实战(二):JVM分代模型:年轻代、老年代、永久代_第13张图片 

4、如何在启动系统的时候设置JVM参数?

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

  • 采用“java -jar”的方式启动一个jar包里的系统,那么就可以采用类似下面的格式:
java -Xms512M -Xmx512M -Xmn256M -Xss1M -XX:PermSize=128M -XX:MaxPermSize=128M -jar App.jar
  •  Spring Boot其实就是启动的时候可以加上JVM参数。
  • Tomcat就是在bin目录下的catalina.sh中可以加入

5、每日百万交易的支付系统,如何设置JVM堆内存?

Version:0.9 StartHTML:0000000105 EndHTML:0000000584 StartFragment:0000000141 EndFragment:0000000544

1、支付系统背景引入

先来看看,如果在一个电商系统里,一个支付系统大概应该是一个什么样的位置,如下图

JVM实战(二):JVM分代模型:年轻代、老年代、永久代_第14张图片

        假设我们在一个APP或者一个网站里买东西,大体上都是对一些商品加到购物车里,然后下个订单,接着对订单进行支付,钱从我们的账户划拨到人家网站的账户里去,大致如此。

        所谓的 支付系统,是一个网站或者APP后台系统中非常核心的一个环节,负责管理公司
的资金流。它负责对接用户的支付请求,然后根据用户的付款方 式, 跟第三方的支付渠道对接起来,比如微信、支付宝 ,等

        比如用户通过微信付款88元,那么他的钱在微信钱包里,需要划拨88块到电商公司账户里去,此时支付系统就得跟微信支付渠道对接,资金从微信划拨过来,大概的流程就是这个意思。

2、支付的核心业务流程

JVM实战(二):JVM分代模型:年轻代、老年代、永久代_第15张图片

  •  首先用户在我们的商城系统提交支付一个订单的请求,接着商城系统把这个请求提交给支付系统,支付系统就会生成一个支付订单,此时订单状态可能是“待支付”的状态。
  • 然后支付系统指引用户跳转到付款页面,选择一个付款方式。
  • 然后用户发起实际支付请求,支付系统把实际支付请求转交给第三方支付渠道,比如微信或者支付宝,它们会去处理支付请求进行资金转移。
  • 如果微信或者支付宝处理完支付之后,就会返回支付结果给支付系统,支付系统可以更新自己本地的支付订单的状态变成“已完成”。
当然,其实一个完整的支付系统还包含很多东西。比如还要负责对账以及跟合作商户之间的资金清算,支付系统得包含应用管理、账户管理、渠道管理、支付交易、对账管理、清算管理、结算管理,等各种功能模块,但是我们这里就关注最核心的支付流程即可。

3、每日百万交易的支付系统的压力在哪里?

      比如上面的那个核心支付流程,我们的这套系统每日要发生百万次交易。 一般达到百万交易要不然是国内最大的互联网公司,要不就是一个通用型第三方支付平台,对接各种APP的支付交易。
        上述业务流程中, 最核心的环节,就是在用户发起支付请求的时候,会生成一个支付订单
这个支付订单需要 记录清楚比如是谁发起支付?对哪个商品的支付?通过哪个渠道进行支付?还有发起支付的时间 ?等等,诸如此类的信息。
如果每日百万交易,那么大家可以想象一下, 在我们的JVM的角度来看,就是每天会在JVM中创建上百万个支付订单对 象。 如下图:

JVM实战(二):JVM分代模型:年轻代、老年代、永久代_第16张图片

        我们的支付系统,其实他的压力有很多方面,包括高并发访问、高性能处理请求、大量的支付订单数据需要存储,等等技术难点。

        在 JVM 层面,我们的支付系统最大的压力,就是每天JVM内存里会频繁的创建和销毁100万个支付订单,所以这里就牵扯到一个核心的问题
  • 我们的支付系统需要部署多少台机器?
  • 每台机器需要多大的内存空间?
  • 每台机器上启动的JVM需要分配多大的堆内存空间?
  • 给JVM多大的内存空间才能保证可以支撑这么多的支付订单在内存里的创建,而不会导致内存不够直接崩溃?
这就是我们本文要考虑的核心问题。

4、支付系统每秒钟需要处理多少笔支付订单

        要解决线上系统最核心的一个参数,也就是 JVM堆内存大小的合理设置 ,我们首先第一个要计算的 ,就是每秒钟我们的系统要处理多少笔支付订单。
        
  • 假设每天100万个支付订单,那么一般用户交易行为都会发生在每天的高峰期,中午或晚上。
  •  假设每天高峰期大概是几个小时,用100万平均分配到几个小时里,那么大概是每秒100笔订单左右,咱们就以每秒100笔订单来计算一下好了。
  •  假设我们的支付系统部署了3台机器,每台机器实际上每秒大概处理30笔订单。
大家看下面的图,这个图可以反映出来 支付系统每秒钟的订单处理压力

JVM实战(二):JVM分代模型:年轻代、老年代、永久代_第17张图片

5、每个支付订单处理要耗时多久?

        如果用户发起一次支付请求,那么支付需要在JVM中创建一个支付订单对象,填充进去据,然后把这个支付订单写入数据库,还可能会处理一些其他的事情咱们就
假设一次支付请求的处理,包含一个支付订单的创建,大概需要1秒钟的时间
重要:每台机器一秒钟接收到3 0笔支付订单的请求,然后在JVM的新生代 里创建了30个支付订单的对象,做了写入数据库等处理接着1秒之后,这30个支付订单就处理完毕,然后对这些支付订单对象的引用就回收了,这些订单在JVM的新生代里就是没人 引用的垃圾对象了。 接着再是下一秒来30个支付订单,重复这个步骤。

6、每个支付订单大概需要多大的内存空间?

直接根据支付订单类中的实例变量的类型来计算就可以了。
比如说支付订单类如下所示,你只要记住一个 Integer类型的变量数据是4个字节,Long类型的变量数据是8个字节,还有别的类型的变量数据占据多少 字节 。然后就可以计算出每个支付订单对象大致占据多少字节。 JVM实战(二):JVM分代模型:年轻代、老年代、永久代_第18张图片

        一般来说,比如支付订单这种核心类,你就按20个实例变量来计算,然后一般大概一个对象也就在几百字节的样子。我们算他大一点好了,就算一个支付订单对象占据500字节的内存空间,不到1kb。

7、每秒发起的支付请求对内存的占用

        假设有3台机器,每秒钟处理30笔支付订单的请求,那么在这1秒内,大家都知道,肯定是有方法里的局部变量在引用这些支付订单的,如下图: JVM实战(二):JVM分代模型:年轻代、老年代、永久代_第19张图片

        那么30个支付订单,大概占据的内存空间是30 * 500字节 = 15000字节,大概其实也就15kb而已。其实是非常非常小的。

8、让支付系统运行起来分析一下

  • 每秒30个支付请求,创建30个支付订单对象,也就占据kb级别的内存空间而已。然后接着1秒过后,这30个对象就没有人引用了,就成了新生代里的垃圾了。
  •  下一秒请求过来,我们的系统持续的创建支付订单对象,不停在新生代里放入30个支付订单,然后新生代里的对象会持续的累积和增加。
  • 直到有一刻,发现可能新生代里都有几十万个对象了,此时占据了几百MB的空间了,可能新生代空间就快满了
  • 然后就会触发Minor GC,就把新生代里的垃圾对象都给回收掉了,腾出内存空间,然后继续来在内存里分配新的对象。
这就是这个业务系统的运行模型。

9、对完整的支付系统内存占用需要进行预估

        之前的分析,全部都是基于一个核心业务流程中的一个支付订单对象来分析的,其实那只是一小部分而已。
         真实的支付系统线上运行,肯定每秒会创建大量其他的对象,但是我们结合这个访问压力以及核心对象的内存占据,大致可以 来估算一下整个支付系统每秒钟大致会占据多少 内存空间。
        其实如果你要 估算的话,可以把之前的计算结果扩大10倍~20倍 。也就是说, 每秒钟除了在内存里创建支付订单对象,还会创建其他数十种对 象。那么每秒钟创建出来的被栈内存的局部变量引用的对象大致占据的内存空间就在 几百KB~1MB 间。
        然后下 一秒继续来新的请求创建大概1MB的对象放在新生 代里,接着变成垃圾,再来下一秒。 循环多次之后,新生代里垃圾太多,就会触发Minor GC回收掉这些垃圾。这就是一个完整系统的大致JVM层面的内存使用模型。

10、支付系统的JVM堆内存应该怎么设置?

        每个机器上部署上线的时候,JVM的堆内存应该如何设置了。
  • 其实一般来说这种线上业务系统,常见的机器配置是2核4G,或者是4核8G
  • 如果我们用2核4G的机器来部署,那么还是有点紧凑的,因为机器有4G内存,但是机器本身也要用一些内存空间,最后你的JVM进程最多就是2G内存。
  •  然后这2G还得分配给方法区、栈内存、堆内存几块区域,那么堆内存可能最多就是个1G多的内存空间。
  • 然后堆内存还分为新生代和老年代,你的老年代总需要放置系统的一些长期存活的对象吧,怎么也得给几百MB的内存空间, 那么新生代可能也就几百MB的内存了。
  • 这样的话,大家可以看到,我们上述的核心业务流程,只不过仅仅是针对一个支付订单对象来分析的,但是实际上如果扩大10倍~20倍换成对完整系统的预估之后,我们看到,大致每秒会占据1MB左右的内存空间。
        那么如 果你新生代就几百MB的内存空间,是不是会导致运行几百秒之后,新生代内存空间就满了?此时是不是就得触发Minor GC 了?
        其实如果这么频繁的触发Minor GC,会影响线上系统的性能稳定性。
        因此你可以考虑采用4核8G的机器来部署支付系统,那么你的JVM进程至少可以给4G以上内存,新生代在里面至少可以分配到2G内存空间。这样子就可以做到可能新生代每秒多1MB左右的内存,但是需要 将近半小时到1小时才会让新生代触发Minor GC, 这就大大降低了GC的频率。
        举例:机器采用4核8G,然 后-Xms和-Xmx设置为3G,给整个堆内存3G内存空间,-Xmn设置为2G,给新生代2G内存空间。 而且假设你的业务量如 果更大,你可以考虑不只部署3台机器,可以横向扩展部署5台机器,或者10台机器,这样每台机器处理的请求更少,对JVM的压力更小。

11、本文总结

系统在日百万交易的压力下,部署3台机器的场景下,每秒钟每台机器需要处理多少笔订单,每笔订单要耗时多久处理,每秒钟会对JVM占据多大内存空间,根据这个横向扩展预估整个系统每
秒需要占据多大内存空间。 接着根据上述数据模型推算出,在不同的机器配置之下,你的新生代大致会有多大的内存空间,然后在不同的新生代大小之下,多久会触发一次Minor GC。为了避免频繁的GC,那么应该选用什么样的机器配置,部署多少台机器,给JVM堆内存多大的内存空间,新生代多大的内存空间。根据这套配置,就可以推算出来整个系统的运行模型了,每秒钟创建多少对象在新生代,然后1秒之后成为垃圾,大概系统运行多久,新生代会触发一次GC,频率有多高。

6、JVM设置不合理的案例

        如果支付系统因为没有经过合理的内存预估,所以直接选用了1台2核4G的虚拟机来部署了线上系统,而且就只用了一台机器。然后线上JVM给的堆内存大小,仅仅就只有1G,扣除老年代之后,新生代其实就几百MB的内存空间,

JVM实战(二):JVM分代模型:年轻代、老年代、永久代_第20张图片

        就是每天100万交易,高峰期每秒大概100笔支付交易,对应核心的支付订单对象有100个创建出来,每个支付订单对象占据500左右的字节大小,总共就是50kb左右。然后一笔交易要1秒来处理,所以这100个对象在新生代中存在1秒的期间会被人引用,是无法被回收的。 而且我们之前说过一个全局预估的思路,从核心的支付订单对象扩展开来,拓展到系统里其他的对象中去,起码可以把内存占用扩大了10倍~20倍比如我们就扩大个20倍好了,那么说明1秒之内,总共会创建出来大概1MB左右的对象,无法被回收

大促期间,瞬时访问量增加十倍
        其实按照估算出来的内存压力,你这么小的新生代在系统正常运行的情况下,其实还不算什么大问题。 因为每秒新增1MB对象,然后几百秒过后,新生代快满了,自然就会触发Minor GC,回收掉里面99%的垃圾对象。你要是内存那么小,最多就是发现系统每隔几分钟略微卡 顿一下,因为这个时候在进行垃圾回收,会影响系统性能。
    
     但是现在我们假设,如果你的电商系统搞大促活动呢?
        一般搞大促活动,很可能导致你的压力瞬间增大10倍,此时可能会发现,每秒钟你的支付系统不是100笔订单了,可能是每秒钟上千笔订单。这个时候你的系统压力本身就会很大了,不光是内存,尤其是线程资源、CPU资源,都会几乎打满。内存就更是岌岌可危了
 
少数请求需要几十秒处理,导致老年代内存占用变大
        现在假设你每秒1000笔交易,那么每秒钟系统对内存的占用增加到10MB以上。我们甚至可以再大胆一点, 预估每秒对内存占用达到几十MB,甚至上百MB也可以 ,因为毕竟大促时流量激增,
而且最可怕的一点是,可 能你每秒过来的1000笔交易,不再是1秒就可以处理完毕了,因为压力骤增,会导致你的系统性能下降,可能偶尔会出现每个请求处理完毕需要几秒钟,甚至几十秒的时
        此时我们看下图可能出现什么问题,假设你的新生代里已经积压了很多的数据,都快满了。

JVM实战(二):JVM分代模型:年轻代、老年代、永久代_第21张图片

然后此时内存里有比如几十MB的对象都被人引用着,因为少数请求突然处理的特别慢。

为什么会处理特别慢?因为压力太大,导致系统性能太差了,如下图。

JVM实战(二):JVM分代模型:年轻代、老年代、永久代_第22张图片

        这个时候,如果你要再次在新生代里分配对象,那么是不是会导致一次Minor GC去回收新生代? 没错,但是可能回收掉大量的对象之后,那少数几十MB的对象还在,因为少数请求特别的慢。然后很快新生代继续被填满,再次触发Minor GC,然后少数几十MB的对象还在,此时多次之后后,就会被转移到老年代去,如下图。

老年代对象越来越多导致频繁垃圾回收

        上述流程如果反复来多次,就是时不时有少数请求特别慢,创建的对象在新生代反复多次没法被回收,然后就会被弄到老年代去。然后后续处理完之后,老年代里的对象就没人引用了,成为了垃圾对象。
        经常重复这个流程,老年代里的垃圾对象,是不是就会越来越多?
        一旦老年代的垃圾对象越来越多,迟早会满,然后就会触发老年代的垃圾回收,而且这个老 年代被占满的频率还很快,可能就会频繁触发老年代的垃圾回收
        老年代的垃圾回收速度是很慢的。
        但是在上述场景下,我们基本可以分析出来,如果你不合理的设置内存,就会导致新生代内存不充足,然后导致很多对象不停的迁移到老年代去,最后导致老年代也要不停的进行垃圾回收。
最后这频繁的垃圾回收,就会极大的影响系统的性能。
反面案例总结
        通过一个支付系统内存设置过小,然后突发巨大的流量压力,突发的性能抖动,最后导致很多对象长期在新生代被人引用,无法被回收,最后持续进入老年代,最后触发老年代内存都频繁占满,然后老年代都频繁被垃圾回收。
如何合理设置永久代大小?
         一般永久代刚开始上线一个系统,一般你设置个几百MB,大体上都是够用的。 因为里面主要就是 存放一些类的信息
如何合理设置栈内存大小
        其实这 个栈内存大小设置,一般也不会特别的去预估和设置的,一般默认就是比如512KB到1MB,就差不 多够了。 这就是 每个线程自己的栈内存空间,用来存放线程执行方法期间的各种布局变量的
作业:
梳理出来,看看你们线上的真实负载情况,每秒钟多少请求?
然后根据你们的核心业务流程,看看每秒钟你负责的系统对内存使用的压力有多大?
就你的系统内存使用压力之下,目前你们线上机器是多大的堆内存?新生代多大?老年代多大?
然后分析一下,目前的这个内存配置,你们的垃圾回收有多频繁?
老师, 可以说下, 为什么并发上来了, 压力就会剧增嘛? 哪些方面的压力.
并发上来之后,内存、网络带宽、磁盘IO、数据库,都是系统的瓶颈,比如网络带宽打满,你的请求就会排队卡住,磁盘IO变 满,数据库性能下降,都会导致请求处理慢几十倍
 
“可能你每秒过来的1000笔交易,不再是1秒就可以处理完毕了,因为压力骤增,会导致你的系统性能下降,可能偶尔会出现每个请求处理完毕需要几秒钟”:老师,这里说的压力骤增是磁盘读写压力吗还是内存CPU压力,出行每个请求处理完毕需要几秒这里是写入压力吗?与网络有关吗?谢谢
都有可能,主要是CPU负载过高,会导致高并发下每个请求的处理性能直线下降,还有网络问题也会有
 
付订单对象,还创建其他数十种对象。那么计算的方式是不是:30 * 500字节/个 * 放大20倍≈300000字节≈300KB ? ||
1、支付系统高分期需要处理的请求是是不是应该这么算:100万 / (24 * 3600) ≈ 12,根据28法则,大部分请求发生在中午12点到13点以及晚上的18点到19点,所以 80万请求 / (2 * 3600) ≈ 111,即算出如果单台每秒大概是100多个请求
2、还有就是在完整的支付系统内存占用需要进行预估中,你提到“可以把之前的计算结果扩大10倍~20倍。也就是说每秒除了内存里创建的支付订单对象还创建数十种对象” 这里如果要计算的话 之前的计算结果是 30 * 500字节 * 20倍 = 300000字节=300KB 是这么算吗?

你可能感兴趣的:(JVM,java)