JVM内存模型能够分为两个部分,以下图所示,堆和方法区是全部线程共有的,而虚拟机栈,本地方法栈和程序计数器则是线程私有的。下面咱们就来一一分析一下这些不一样区域的做用。java
堆内存是全部线程共有的,能够分为两个部分:年轻代和老年代。下图中的Perm表明的是永久代,可是注意永久代并不属于堆内存中的一部分,同时jdk1.8以后永久代也将被移除。github
GC(垃圾回收器)对年轻代中的对象进行回收被称为Minor GC,用通俗一点的话说年轻代就是用来存放的年轻的对象,年轻对象是什么意思呢?年轻对象能够简单的理解为没有经历过屡次垃圾回收的对象,若是一个对象经历过了必定次数的Minor GC,JVM通常就会将这个对象放入到年老代,而JVM对年老代的对象的回收则称为Major GC。算法
如上图所示,年轻代中还能够细分为三个部分,咱们须要重点关注这几点:jvm
import java.util.ArrayList; import java.util.List; /* java -Xms20m -Xmx20m HeapOOM */ public class HeapOOM { static class OOMObject { } public static void main(String[] args) { List<OOMObject> list = new ArrayList<OOMObject>(); while (true) { list.add(new OOMObject()); } } }
下面是执行结果:性能
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Arrays.java:3210) at java.util.Arrays.copyOf(Arrays.java:3181) at java.util.ArrayList.grow(ArrayList.java:261) at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235) at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227) at java.util.ArrayList.add(ArrayList.java:458) at HeapOOM.main(HeapOOM.java:13)
堆内存是咱们平时在生产环境中进行性能调优中的一个很是重要的部分,对于这里我在另一篇文章JVM垃圾回收算法及回收器详解有详细介绍,这里咱们仍是拓展补充几个常见的性能调优参数:优化
方法区与Java堆同样,是各个线程共享的区域,它用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译(JIT)后的代码等数据。spa
对于JDK1.8以前的HotSpot虚拟机而言,不少人常常将方法区称为咱们上图中所描述的永久代,实际上二者并不等价,由于这仅仅是HotSpot的设计团队选择利用永久代来实现方法区而言。同时对于其余虚拟机好比IBM J9中是不存在永久代的概念的。操作系统
其实,移除永久代的工做从JDK1.7就开始了。JDK1.7中,存储在永久代的部分数据就已经转移到了Java Heap或者是 Native Heap。但永久代仍存在于JDK1.7中,并没彻底移除,譬如符号引用(Symbols)转移到了native heap;字面量(interned strings)转移到了java heap;类的静态变量(class statics)转移到了java heap,而在JDK1.8以后永久代的该概念也已经再也不存在取而代之的是元空间metaspace。线程
常量池实际上是方法区中的一部分,由于这里比较重要,因此咱们拿出来单独看一下。注意咱们这里所说的运行时的常量池并仅仅是指Class文件中的常量池,由于JVM可能会进行即时编译进行优化,在运行时将部分常量载入到常量池中。
JVM中的程序计数器和计算机组成原理中提到的程序计数器PC概念相似,是线程私有的,用来记录当前执行的字节码位置。仍是稍微解释一下吧,CPU的占有时间是以分片的形式分配给给每一个不一样线程的,从操做系统的角度来说,在不一样线程之间切换的时候就是依赖程序计数器来记录上一次线程所执行到具体的代码的行数,在JVM就是字节码。
与程序计数器同样,Java虚拟机栈也是线程私有的,用通俗的话将它就是咱们经常据说到堆栈中的那个“栈内存”。虚拟机栈描述的是Java方法执行的内存模型:每一个方法在执行的同时都会建立一个栈帧(Stack Frame)用于存储局部变量表(局部变量表须要的内存在编译期间就肯定了因此在方法运行期间不会改变大小),操做数栈,动态连接,方法出口等信息。每个方法从调用至出栈的过程,就对应着栈帧在虚拟机中从入栈到出栈的过程。p.s: 关于栈帧这里咱们之后讲虚拟机字节码执行引擎的时候再来仔细分析。
本地方法栈和Java虚拟机栈相似,只不过是为JVM执行Native方法服务,这里就不解释了。
GitHub: https://github.com/ziwenxie
Blog: https://www.ziwenxie.site
本文为做者原创,转载请于开头明显处声明我的博客出处 :-)