00:00
前面的话呢,咱们已经讲清楚了,我们创建了一个对象之后呢,在堆空间当中是如何分配它的,呃,这个空间的一个一般步骤,主要呢,针对的是我们,诶看到的这个图。这呢是我们讲解的一个一般的一个过程,对吧?那么在这个图呢,讲解完以后呢,咱们也稍微呢给大家说了一下,就是对于一些特殊的情况,所谓的特殊情况呢,就是说如果我们遇到这个对象呢,比较大,这个大对象的话呢,我们可能会考虑是是把它直接呢,就放在我们的养老区啊,对吧?诶这是这样这样的问题,这呢是咱们说的其中的一种特殊情况,那么整体总结一下的话呢,我们的内存分配策略是什么样子的呢?这呢是我们这个小结呢,想给大家做一个说明,这呢是一个总结性的一个内容,诶需要呢,大家能够掌握关注这样的问题,内存分配的策略。好,那首先呢,这块来说明的内容啊,就是咱们前面讲的这个一般情况,那么一般情况是什么样子呢?就是说我们创建了一个对象,这个对象啊,是不是优先呢,要分配在我们的新生代中的伊甸园区啊,对吧?那伊甸园区的话呢,如果在进行跟这个内存空间不足,我们进行mini jc的时候呢,还存活了,我们放在这个survival区,其中呢,有专门的一个我们叫做年龄计数器去记录一下它在这个新生代当中,如果每次呢都存活,我们呢去增加他的这个年龄,诶默认情况下呢,如果这个年龄咱们这个阈值呢,超过15岁的时候,他呢,是不是要晋升到我们这个老年代啊,这呢是咱们前面说的这个一般情况,哎,不多说了,然后接下来呢,我们整体来看一下这里边的这些描述,那这些描述呢,是咱们这个,诶GM这个官方啊,说明到的这个对象提升的一个规则,或者呢,也称为叫内存分配的策略。
01:42
提升叫promotion升值啊,也叫promotion好,那我们这几个规则,这个原则大家看一看,首先优先分配到伊甸园区,这个不需要多说吧。那伊甸园区呢,咱们存放的对象呢,都大多数啊,都是朝生夕死的,那么我们在放对象的时候呢,咱们这个没办法判定说他是不是朝生夕死的,咱们就先把它都放在这个叫异甸园区,那相对来说呢,这个空间呢,也算是咱们新生代中最大的一个空间了,对吧,这是这个,呃,不多说啊,第二个来说大对象呢,直接分配到老年代。
02:15
大对象直接分配老年代就是这个大对象呢,呃,我们通常呢,指的是在这个内存空间当中,它是一个连续的,需要一个呃,比较长的一个连续的内存空间的一个对象,那遇到这样的对象的话呢,我们如果说新生代中的伊甸园区都放不下了,那通常呢,我们说这个伊甸园区是比我们这个survivor区是不是还要大一些,那如果说这个伊甸园区都放不下了,那咱们只能考虑把它直接放在这个老年代当中。啊,是这样子的,那么通常在我们开发当中比较长的这个字符串啊,或者是数组啊,都属于我们所要谈的这个大对象。啊,都属于这个大对象,那这儿呢,我写了一句话说呢,咱们在这个开发当中啊,大家尽可能的要避免出现过多的这个大对象。来避免出现过多的大对象,那这里边呢,就出现一个情况,就是说,呃,这个我们写程序中比较糟糕的事情呢,就是出现这个大对象,因为呢,它需要这个叫连续的内存空间,那有可能咱们这个比如说啊,比如说咱们这个伊甸园区也好,还是说这个老年代也好啊,它还控制一些空间,但是这个空间呢,不足以放下咱们这个大对象,那是不是就会导致我们这儿呢,叫mid j c,那这块的话会导致我们叫me j c是吧?呃,就是其实空间呢,剩的还不少,但是呢,你这是个大具象,所以呢,就会导致我们进行这个GC,那1GC的话呢,是不是就会STW啊。
03:34
哎,那从这个角度上来讲呢,咱们尽量呢,你少创建这个大对象。啊,那这个比创建这个大对象呢,更糟糕的事情是什么呢?就是你创建这个过多的大对象呢,它竟然是朝生夕死的。啊,这个我花了很多的时间呢,给你去做这个JC啊,还是出现了这个SWSTW,然后最后呢,你刚存下来,下一步呢,在JC的时候就不用了,很悲痛是吧,很很悲伤,就像这个小品里边说的这句话啊,说呢,人生最痛苦的事情呢,是人没了钱还在。
04:07
啊,这个人生最最痛苦的事情呢,是这个人还在钱没了是吧,就像我们这里边儿这个痛苦的事情呢,是我们程序中出现了过多的这个大对象,那更痛苦的事情呢,就是过多的这个大对象呢,竟然是朝生夕死的。啊,这个大家要注意,我们写程序的时候呢,尽量要避免这个问题,好,那下一个呢,说长期存活的对象呢,分配到这个老年代当中。那这里边指的这个长期存活的对象呢,就可以理解成是我们经过这个阈值啊,叫年龄计数器不断的去增加,超过这个阈值的话呢,那我们就证为证明他是一个久经考验的这个战士,对吧?哎,我们呢,就把它放在这个老年代当中说,那你就没有必要呢,经常性的进行这个JC了。啊,也有点像咱们这个疫情呢,你说这个有一个从湖北来的一个朋友是吧,这个人呢,这个经常呢,进行这个试纸的测试,都连续测试了15次了,都没有这个,呃,阳性这个显示对吧,那再测一次发现也没有,没有,那就直接呢,你就是个安全的,咱以后就不怎么去测你了。
05:06
诶,就属于这样的一个情况啊,就是这个,哎,长期存活的对象呢,我们就把它放在老年代了啊,尽量降低这个GC的一个频率。好,然后下边这个呢,叫动态对象年龄判断,这个呢大家是不清楚的,来看一下什么意思啊,前面呢,咱们讲到说呢,呃,有当这个年龄计数器的这个值达到15以后,我们呢,才考虑是不是把它晋升到我们的老年代,对吧,那是这样的一个情况,那事实上的话呢,我们说呃,也不是说非得是要达到那个预知15,那这里边写的说如果呀,咱们这个幸存者区中相同年龄的所有对象。相同年龄的,比如说年龄都是五,那这个对象的总和呢,大于咱们这个vival空间弄到一半了,说你比我这一半的空间还要大,那这个时候呢,咱们把这个大于这个平均这个这个这个年龄的,比如说刚才说的是五大于这个五这个年龄啊,或者是等于这个年龄的,我们就直接呢,把他们放到这个老年代。
06:07
那无需呢,等到我们这个达到这个阈值这样一个要求。那这呢,还有这样一个特殊情况啊,大家怎么去理解这个事儿呢?你想想我们这个survivor区,咱们呢是有两个空间,是不是每次进行完样JC之后呢,咱们这俩空间呢,都得是你导到我这儿,我导到你这儿,这呢就是咱们后边要讲的这叫复制算法了,那你想想这个相同年龄的这个对象呢,还挺多的,占到一半的空间,那每次这样倒的话呢,可能你想想它既然能达到一半,说明他这块呢,还是呃,基本上很难被回收掉的,是吧,每次这样呢,倒腾这个剩余的空间都很小,而且呢,翻来覆去的进行复制,这个呢还是挺耗时的,所以呢,既然如此,咱们就把这个年龄大于或等于这个,诶你这这个这个A的对象呢,我们就直接呢提前放到这个老年代当中,这呢也是一个优化。啊是一个分配上的一个策略啊行这块呢,大家关注一下。然后下边呢,提到一个叫空间分配担保。
07:02
嗯,空间分配担保这块呢,我们会涉及到一个参数,这个参数呢,咱们等到这个咱们后边这个章,后边这个小结的时候,咱们系统性的去总结一下咱们整个这堆空间中的这些参数的设置,诶到那个时候呢,咱们详细的再给大家把这个参数呢说明一下。啊,这儿呢,我们先泛泛的来说一下,什么叫空间分配担保呢?啊,应该是这样子啊,比如说咱们在这个大量的对象在这个GC之后呢,仍然是存活的,当然最直最这个极端的一个例子呢,就是说我们进行mirror jc之后呢,所有的这个对象都存活是吧?这是一个极端的例子啊,但咱们又知道呢,这个survivor区相对来说是不是就比较小啊?哎,那这个时候呢,咱们就需要老年代呢,进行一个这个空间分配担保,把这个survivor这个区当中无法容纳的这个对象呢,咱们就放到这个老年代了。啊,就放到这个老年代了,那也就是说呢,这个老年代呢,仅仅空间分配间担保,它的前提呢,就是这个老年代呢,它得有这个空间能够去容纳这些对象。
08:01
就有点像呢,大家呢,去这个你想急需一个亿是吧,哎直接呢,你找到了当地一个小银行,说呢,你给我一个亿啊做个担保,那小银行的话呢,根本就没有这样的能力给你拿出来一个亿的现金是吧,他担保不了,哎这也不行。啊,那么在这个担保过程当中啊,我们需要用到这样的一个参数,就是他这个晋升的一个,呃失败看看他是出还是false啊这里边有一些细节,诶咱们放到这个后边的时候呢,再说,诶这整体呢,就是咱们所谓的这个叫诶这个内存分配的一个策略,这样的几个规则。在咱们这个GM这个规范当中呢,都有写,所以大家呢,把这几个规则呢,需要记一下啊,去理解是吧,理解一下,那这里边呢,给大家稍微演示一个代码啊,就是这里提到说这个大对象呢,直接分配到这个老年代的一个行为,呃,这个代码也比较简单啊,看一下我这呢,在main方法当中直接呢造了一个呃对象,这个对象呢是一个数组,嗯,刚才咱们说的大对象呢,一个典型的场景呢,就是一个比较大的数组,或者是一个字符串,这个字节数组的长度呢,我这呢是20兆。
09:02
20兆对吧,那凡是咱们new的这个对象啊,New的这个结构呢,都是放在堆空间中的,这个大家呢要注意,那我们这个堆空间呢,大小怎么分配啊,我呢堆空间整个呢是60兆,然后呢,这个新生代和老年代的比例呢,是1:2,默认呢也是1:2,然后这个survivor呃,伊甸园区跟survivor的比例呢,是八,也算是这个默认值,那这个时候呢,我们整个分配下来呢,这个伊甸园区多少啊。它应该是占这个16兆是吧,然后survivor呢,这是两兆,这个survivor呢,这个是两兆,然后我们这个O的区呢,应该是占这个40兆,算一算对吧,真的是20,真的是40,诶这个比例是8:1:1行,那么这时候大家看我们造的这个数组的空间呢,是20兆,那20兆的话呢,你往这个伊甸园区所外区都放不下,那我们此时呢,就相当于直接进入老年代。哎,也就是说呢,大对象直接进入老年代,好,那此时呢,咱们把整个这个参数,注意一定要把后边这个参数加上,咱们在这呢做一个配置。
10:02
嗯,咱们现在想演示的是这个一样是吧,好它。嗯,CTRL一下。粘过来以后呢,做一个应用,OK跑一下。出来了行,那么这块的话呢,我们首先需要做个说明,此时呢,咱们先打印一下这个JC的细节,咱们在上一个代码当中是不是也用过这个参数,如果在我们显示最后这个堆空间这个比例的前前边你出现过这个垃圾回收的话呢,是不是垃圾回收的那个日志呢,也会出现啊,咱们上个例子呢,就演示过对吧,你上面这个,呃,这个我们再看一下哈,刚才咱们演示的是这个JC是这个例子。嗯,这个例子咱们还得再再调一下,想说明的是这个问题。嗯,JC它是吧,嗯,这个咱们还得改一下这个参数啊。哎,这个大家应该能明白,我我要说的是什么意思。
11:09
跑一下。好,来看这时候呢,我们发现上面不是出现这个异常了嘛,对吧,然后下边这块呢,这不就打印了我们这个目前这个堆空间中各个区域的一个内存占用情况,然后在这个前面的话呢,我们会有这个JC的一个日志,也就是说呢,我们这个程序在执行过程当中,你进行过几次这个GC这块呢,都有记录啊,然后最后呢,来显示一下我们这个对空间的一个占比情况,这个呢,是是由我们这个叫print j c details这个参数上来控制的,好那么看一下咱们刚才用的这个参数。And it?恩,找到咱们这个药。针对他把这个呢粘过来。听一下。画一下。此时的话呢,当然你发现咱们这个在显示这个对空间上边是没有任何的这个JC日志,那就意味着咱们没有进行过这个垃圾回收,哎,没有出现过这垃圾回收,这这个比例呢,跟咱们刚才说的是完全一样子的,然后大家呢,关注一下我们现在是20兆啊20兆,你看这个20兆的空间呢,是在哪占用的。
12:15
嗯,20兆的话呢,就是2048K20480K20兆嘛,对吧,那大家会发现呢,是不是这个空间呢,是出现在我们这个叫old区,也就是我们说的老年代。嗯,对于咱们这个,呃,伊甸园区from区two区来讲,是不是谁也盛不下这么大的一个空间,他也并没有说把我们这个速度给肢解了,是吧,没法肢解,咱们前面呢,也没有垃圾回收说说明了就是直接一步到位,直接进入到我们这个老年代的。哎,这呢,就验证了咱们说的是这样一个问题。OK啊,其他的这块呢,就不用多说了。行这呢,就是我们说的这个内存分配的一个策略啊,大家呢,需要掌握啊,这里边儿的几个规则。
我来说两句