28.JVM运行时数据区之堆区概述
1.堆(Heap)核心概述
堆是JVM运行时数据区中的重点内容。
1.堆区是进程私有的。一个进程对应一个JVM
实例。一个JVM
实例中有一个Runtime
实例(Runtime
实例对应的就是运行时数据区),里面包含了运行数数据区的各个部分(堆区、方法区、虚拟机栈、程序计数器、本地方法栈)。一个JVM实例都有自己的一个堆区。
2.一个进程包含多个线程,线程共享了堆区和方法区,同时各个线程拥有自己的虚拟机栈,本地方法栈,程序计数器。
3.堆区在JVM启动的时候被创建。
4.堆区的大小可以被调节。
5.堆区被所有的线程共享。还可以划分出线程私有的缓存区(Thread Local Allocation Buffer, TLAB)
6.几乎所有的对象实例以及数组都是在堆上分配的空间。之所以是几乎,是因为存在栈上分配的情况(后面的博客会讲到)。
7.数组和对象可能
永远不会存储在栈上,栈帧中保存的是引用(局部变量表中存放的引用),这个引用指向的是对象或者数组在堆中的位置。
8.在方法结束执行之后,堆中的对象不会被立即被移除,需要等待垃圾回收器来回收。
9.堆是GC
的重点区域。
2.堆与栈、方法区之间的关系
public class SimpleHeap { private int id;//属性、成员变量 public SimpleHeap(int id) { this.id = id; } public void show() { System.out.println("My ID is " + id); } public static void main(String[] args) { SimpleHeap sl = new SimpleHeap(1); SimpleHeap s2 = new SimpleHeap(2); // int[] arr = new int[10]; //Object[] arr1 = new Object[10]; } }
上面的java
代码,在堆空间中创建了两个对象s1
和s2
。
1.Java栈的栈帧中的成员变量表存储着s1
和s2
这两个实例的引用。
2.堆区存储着s1
和s2
对象的实例。
3.方法区中有着关于堆区中s1
和s2
实例的类SimpleHeap
的相关信息以及方法的实现。
3.堆的内存细分
1.现代垃圾收集器大部分都是基于分代收集理论设计。
2.在Java7及之前堆内存逻辑上分为三部分:新生代+老年代+永久区。事实上堆空间不包括永久代。
3.在Java8及之前堆内存逻辑上分为三部分:新生代+老年代+元空间。事实上堆空间不包括元空间。
约定:新生区=新生代=年轻代;养老区=老年区=老年代;永久区=永久代。
4.JDK7
及以前堆空间内部结构:
在Java7
及之前堆内存逻辑上分为三部分:年轻代+老年代+永久区。事实上堆空间不包括永久代,它是属于方法区的具体实现。
也就是说堆空间实际上只包含新生代和老年代两个部分。JDK8
及以后堆空间内部结构:
在Java8
及之前堆内存逻辑上分为三部分:年轻代+老年代+元空间。事实上堆空间不包括元空间,它是属于方法区的具体实现。
也就是说堆空间实际上只包含新生代和老年代两个部分。
5.堆空间内部结构
a)在Java8
之前,堆空间逻辑上划分为新生代(New Gen)
、老年代(Old Gen)
和永久代(Perm Gen)
。
b)在Java8
及之后,堆空间逻辑上划分为新生代(New Gen)
、老年代(Old Gen)
和元空间(Metaspace)
。永久代变成了元空间。
c)其中新生代又划分为伊甸园区(Eden)
和生存区(Survicor)
。