概述
本文将结合自身对JVM的学习,对java虚拟机内存的各个区域阐述个人理解。
首先我们需要了解的是,JVM是《java虚拟机规范》定义的一种规范或标准,而我们在实际应用当中要讨论的是落地的实现,即HotSpot(常用的JVM之一)。
1.运行时数据区
我们知道java在执行一个main函数时,操作系统就会为jvm这一个应用程序分配一块内存空间用于运行咱们的程序代码。那么这块内存在运行起来后,是怎么管理和分区的呢?如下图
从数据的共享/私有属性来看,其中虚拟机栈、本地方法栈和程序计数器(PCR)属于线程私有区域,即这些区域每个线程都各自有一份,且相互独立、互不影响;除此之外,方法区和堆则是属于共享区域,即每个线程都可以访问。
那么他们分别有什么作用呢?
1.1 程序计数器
程序计数器Program Counter Register(简称PCR),它是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。在多线程并发执行时,CPU在不同线程之间来回切换后,为了保证程序的有序进行,CPU需要知道上一次执行到了哪一行代码,而我们的PCR的主要作用就在于此。所有的分支、循环、跳转、异常处理等基础功能都需要依赖它来完成。
如果线程执行的是常规java方法,那么它记录的是正在执行的虚拟机字节码指令的地址;若是native方法,则记录为空(Undefined)。
同时,此内存区域是唯一一个在JVM规范当中没有规定任何OOM的区域。
1.2 虚拟机栈
虚拟机栈,描述的是一个java方法被执行的线程内存模型:一个方法在被执行时,虚拟机就会创建一个栈帧(Stack Frame)用于存储【局部变量表】、【操作数盏】、【动态链接】和【方法出口】等信息。一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈从入栈到出栈的过程。
同样当一个方法调用到另一个方法时,也会创建另外一个栈帧。尤其在进行递归调用时,随着递归的深度越深,创建的栈帧数量也就会越多,当递归深度大于了虚拟机所允许的深度,将会抛出我们常见的StackOverFlowError。
1.2.1 局部变量表
TODO
1.3 本地方法栈
其作用和虚拟机站一样,其区别是它只作用于native方法;而虚拟机栈作用的是常规java方法,也即字节码对应的方法。不过在我们讨论的HotSpot中,直接将二者合二为一了。
1.4 Java堆
Java堆,是虚拟机管理的内存中最大的一块。它是所有线程共享的一块内存区域,在虚拟机启动时创建,用来存放对象实例。因此我们常说的垃圾收集器(GC)也就是作用与这个区域。
随着jdk版本和GC的不断迭代,java堆的设计模型也有一些悄然变化。在G1(Garbage First,垃圾回收器的一种)以前,java堆的设计都是基于“分代模型”来设计,也就是我们常说的:年轻代、老年代、永久代等。但在G1以后,java堆的设计就不再是“分代”了,而是分区。
这里所说的“分代模型”当然也值得总结一番,通常分为:
1.4.1 年轻代
包含eden伊甸区 和两个 surviver幸存区。用于存放刚创建的对象,或是一些“年轻的”对象(即经历GC次数较少)。
1.4.2 老年代
用于存放一些“老”对象,即经历过N多次GC仍未被清理掉的引用对象。以及在JDK1.7中,原本存放于永久代的“字符串常量池”和“静态变量”也开始存放于此。
1.4.3 方法区
通常它用于存放:类加载后的class信息、常量池、静态变量、即时编译器编译后的缓存代码等。
当然,需要注意的是,这里指的方法区其实是JVM定义的一种标准。通常它与java堆需要区分开来理解,写在这是因为在JDK1.8之前的版本,在HostSpot的实现中,它对应的就是永久代;而在JDK1.8(含1.8)以后的实现则是元空间(Meta Space)。元空间默认是受限于物理内存的,也就是说受限于实际的硬件资源大小。
1.5 运行时常量池
运行时常量池,是方法区的一部分。Class文件中的常量池表,在类加载后将会被存放到方法区的运行时常量池中。此外,比如String#intern()方法执行时,常量池中若不存在的字符串,也将会被放入此处。
1.6 直接内存
直接内存(Direct Memory) 并不是虚拟机运行时数据区的一部分。但是它也被频繁的使用到。在JDK1.4中加入的NIO(New Input/Output),引用了一种基于通道与缓冲区的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过Java堆里的DirectByteBuffer作为这块堆外内存的引用。这样一来,程序就可以通过这个引用,直接操作堆外内存。这样的好处就在于某些场景中可以显著的提高性能,因为这样避免了Java堆和Native堆的数据来回复制。其实也就是我们常说的“零拷贝”的一种实现,即省去了从内核空间往用户空间拷贝数据资源消耗。
本文主要参考《深入理解Java虚拟机》。