在了解JVM工作原理之前,我们先来了解一个概念——虚拟机(Virtual Machine),见名知意,虚拟机其实就是虚拟的机器,所有的虚拟机都是模拟物理计算机系统环境,提供一个可运行系统软件的平台。虚拟机可简单分为系统虚拟机和 程序虚拟机,知名的VMware、Visual Box就属于系统虚拟机,而程序虚拟机最为典型的就是JVM。
JVM的全称是Java Virtual Machine ,简单理解就是运行Java程序的虚拟机器。它负责执行程序员编写的Java代码,它执行的指令我们称之为Java字节码指令。
JVM运行时区域

图中黄色区域标识JVM运行时区域,下面简单介绍一下各个区域的概念。
1. Program Counter Register(程序计数器)
Program Counter Register是一块较小的内存空间,是当前线程所执行的字节码行号的指示器。 如果线程执行的是一个Java方法,那么计数器记录的是正在执行的虚拟机字节码指令地址; 如果执行的是一个native方法,那么计数器记录的值为空(undefined)。 计数器是唯一一块不会发生OOM的区域。
2. Java Stack (虚拟机栈)
栈也就是人们常说的虚拟机栈(JVM Stacks),它也是线程私有的,生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行时都会创建一个栈帧。并入栈。 一旦完成调用,则出栈。所有的的栈帧都出栈后,线程也就完成了使命. 栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息
局部变量表存储的是基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用类型(reference类型)和returnAddress类型。 其中,long和double占用2个局部变量空间(slot)其余都占用1个。
如果线程请求的栈深度超过虚拟机所允许的深度,会抛出Stack Overflow异常;如果虚拟机栈在动态扩展时无法申请到足够的内存,就会出现OOM的情况。

3. Native Method Stack(本地方法栈)
Native Method Stack与虚拟机栈作用类似,区别在于本地方法栈是为native方法服务,虚拟机栈为Java方法服务。 与虚拟机栈一样,本地方法栈也同样会抛出Stack Overflow和OOM异常。
4. Heap(堆)
堆是JVM内存占用最大,管理最复杂的一个区域,和方法区一样所有线程共享,其唯一的用途就是存放对象实例:所有的对象实例及数组都在堆上进行分配。 堆因为比较复杂,它有自己的内存划分,按照GC分代收集角度来划分。

- 在堆中,按照GC分代收集可分为用新生代和老年代,新生代占用整个Heap空间的1/3,老年代占用整个Heap空间的2/3。
- 新生代使用的是复制算法,老年代使用的是标记压缩算法
- 在新生代中,又分为Eden区(伊甸区)和survivor0区、survivor1区。下面我们把他两简称为s0和s1。 Eden区占用整个年轻代的8/10,s1和s0分别占用年轻代的1/10。s0和s1也叫from区和to区,他们的位置和名分不是固定的,每次minorGC之后有交换,谁为空谁是to,这个具体在后面谈GC的时候再细说。
思考一个问题,默认参数下,如果仅给出eden区40M,求堆空间总大小?
解题思路
- 由于Eden 、S0、S1比例为 8:1:1 ,所以可以得出年轻代总内存为50MB
- 因为年轻代和老年代分别占用总堆内存的 1/3和2/3,另外已知年轻代为50MB,那么可以得出老年代为100MB ,堆内存为 150MB
5. Method Area(方法区)
方法区是JVM规范里面的运行时数据区的一个组成部分,是所有线程共享的内存区域,用来存储用于存储已经被JVM加载的类信息,常量,静态变量等。为了与Heap(堆)区分开来,它还有一个别名叫做“Non-Heap(非堆)”。
在HotSpot虚拟机中,方法区的实现叫做永久代(permanent generation),但是在JDK1.8中永久代(PermGen)被元数据区(Meta Data Space)所取代,并且将空间分配在本地内存中(也叫直接内存)。
方法区永久代可通过-XX:MaxPermSize来设定永久代最大可分配空间,当JVM加载的类信息容量超过了这个值,会报OOM:PermGen错误,但在JDK1.8中,可通过-XX:MaxMetaspaceSize来设定元数据区的最大可分配空间。
切记不要将方法区和永久代或元数据区混为一谈。方法区是JVM规范的一部分,永久代或元数据区是JVM规范中方法区的实现,一个是规范一个是实现,两者不可混为一谈。
深入理解Java虚拟机JVM的工作原理

1216

被折叠的 条评论
为什么被折叠?



