一、前言
Java运行时内存区主要分为 运行时栈(虚拟机栈)、本地方法栈、程序计数器、堆空间、方法区(JDK1.8之后是元空间),今天来聊一聊我们的堆。
一个对象或者数组的创建是在堆空间中完成的,堆的大小是有限的(固定的),所以,必不可少的我们要考虑一下堆的空间分配问题和对象的分配问题。
二、什么是堆区(Heap)
JVM
虚拟机在执行 Java 程序的过程中会把它管理的内存划分成若干个不同的数据区域。
Heap
堆是
JVM
所管理的内存中最大的一块区域,被所有线程共享的一块内存区域。堆区中存放对象实例,“几乎”所有的对象实例以及数组都在这里分配内存
三、什么是新生代和老年代
堆是垃圾回收器的主要管理区域,JVM在划分堆区时,为了更好的进行垃圾回收,因此划分了新生代和老年代。
由上图可知:堆区大小 = 新生代 + 老年代
新生代占整个堆区的1/3,新生代又被分成了两部分:Eden区和Survivor区,Eden区占80%。两个Survivor区各占10%。
老年代占堆区的2/3。
对象在Eden区分配内存空间,在经历一次垃圾回收后,如果对象还存活就会转移至幸存者区,并且对象的年龄会+1,当年龄增加超过15岁时,就会晋升成老年代。
四、创建对象时的内存分配
创建一个新的java对象,在堆区分配内存,那么在堆中是如何分配内存的呢?
首先先了解两种垃圾回收:
- 新生代收集(
Minor GC
/Young GC
):只对新生代进行垃圾收集; - 老年代收集(
Major GC
/Old GC
):只对老年代进行垃圾收集。需要注意的是 Major GC 在有的时候中也代指整堆收集
我们再看如下图:
1、创建对象时,一开始时分配在Eden区,首先先判断Eden区是否放得下,如果可以放下,就会分配内存空间,反之执行一次YGC(Minor GC),对Eden区进行一次清除。
值得注意的是:在经历YGC的过程中,依然存活的对象将会被转移到幸存者区,幸存者区又分为from和to两块区域,它们将存活的对象复制到未使用的
Survivor
空间(from** 或 **
**to**
),然后将当前正在使用的空间完全清除,交换两块空间的使用状态。每次交换时,对象的年龄会加
+1
。
然后会判断幸存者区是否放得下,如果可以放得下,存入from或者to,如果放不下直接晋升老年代,存放在老年代区。
2、在执行完一次YGC后再次判断Eden区是否放的下,如果可以放下,存入Eden区。
3、如果Eden区放不下,判断老年区是否可以放下,可以放下时,分配给老年代。
4、如果老年区放不下进行一次FGC(Major GC),清除过后再次判断,如果可以存入,分配给老年代,否则 OOM(OutOfMemoryError)。
版权归原作者 记得爱蓝色 所有, 如有侵权,请联系我们删除。