JVM 进行时

上一篇中 我讲述了下关于JVM的基础内容,这次呢,主要从JVM的内部存储结构出发....

即:主要了解想堆(Heap)的内存模型

一块是非堆区,一块是堆区。
堆区分为两大块,一个是Old区,一个是Young区。
Young区分为两大块,一个是Survivor区(S0+S1),一块是Eden区。 Eden:S0:S1=8:1:1
S0和S1一样大,也可以叫From和To。

那么对于这样内存来说,一般对象和数组的创建会在堆中分配内存空间,但是堆中有这么多的区域,具体怎么存储呢??

对象创建所在区域

一般情况下,新创建的对象都会被分配到Eden区,一些特殊的大的对象会直接分配到Old区

   在Eden 的空间有限,在达到空间的临界值时,就会需要对内存进行垃圾回收GC(即:Garbage Collect)。这样的GC我们称之为Minor GC,Minor GC指得是Young区的GC,但是并不是每次GC都会全部回收掉,也有些对象会存活下来。这些存活对象会被赋值出来存在S区(Survivor区)。然后在清除Eden中的对象,这样也保证了Eden的区域相对来说比较连续些。

Survivor区详解

Survivor区分为两块S0和S1,也会叫做(from 和 to),占比是一样(1:1),这2个区域在同一个时间必须要有一个是空的,这样利于在进行GC时

例如:一开始只有Eden区和S0中有对象,S1中是空的。
此时进行一次GC操作,S0区中对象的年龄就会+1,我们知道Eden区中所有存活的对象会被复制到S1区,
From区中还能存活的对象会有两个去处。
若对象年龄达到之前设置好的年龄阈值,此时对象会被移动到Old区,没有达到阈值的对象会被复制到S1区。
此时Eden区和S0区已经被清空(被GC的对象肯定没了,没有被GC的对象都有了各自的去处)。
这时候S0和S1交换角色,之前的S0变成了S1,之前的S1变成了S0。
也就是说无论如何都要保证Survivor区域中有个区域是空的。
Minor GC会一直重复这样的过程,知道S1区被填满,然后会将所有对象复制到老年代中。

当然这么操作的话,那么必然Old区也会达到内存的一个临界值,肯定也是会进行GC的,这样的old 的GC叫做:Major GC

Minor GC:新生代
Major GC:老年代
Full GC:新生代+老年代

为什么需要Survivor区?只有Eden不行吗?

      顾名思义,Survivor(存活),大多数的时候 因为程序线程运行的时候,会产生对象,这种对象一般都是朝生夕死的,存活时间比较短,如果都存在Eden区的话,那么很快内存就会满,这样的话GC(Minor GC)的次数就要增加,GC多的话,那么Old区也就会加速填满,这样的话Old区的话GC(Major GC)也会随着增加,这样就可以看做触发了Full GC。老年代的内存空间远大于新生代,进行一次Full GC消耗的时间比Minor GC长得多。GC的时间加长了的话,这样客户端的响应时间拉长。

所以说Survivor区存在的目的主要还是为了减少GC的次数,从而也减少了FULL GC。加速了响应时间。

为什么需要两个Survivor区?

  分2个,而且2个的占比是(1:1),很明显就是为了解决空间的碎片化。如果一个S区的话,例如:一旦Eden 区满了,触发了GC,那么把存活的对象存入S区。下一次的时候有GC了,那么Eden和Survivor各有一些存活对象,如果此时把Eden区的
存活对象硬放到Survivor区,很明显这两部分对象所占有的内存是不连续的,也就导致了内存碎片化。

接下来介绍一个jdk (jvisualvm)自带的一款查看分析内存的工具

https://visualvm.github.io/pluginscenters.html --->选择对应版本链接--->Tools--->Visual GC

启动对应的项目,即可看到对的堆内存的分布 如下:

通过这个工具,可以自己在idea里面创建一个类(如:死循环,创建类对象等),自己手动制造“车祸现场”.....

未完待续,如有叙述不对的地方,各位看官大佬,多多指点...

猜你喜欢

转载自blog.csdn.net/u010200793/article/details/103644284