作为一名Java开发者,掌握JVM的体系结构也是必须的。
说起Java,人们首先想到的是Java编程语言,然而事实上,Java是一种技术,它由四方面组成:Java编程语言、Java类文件格式、Java虚拟机和Java应用程序接口(Java API)。对于JVM的学习,在我看来这么几个部分最重要:
1.Java代码编译和执行的整个过程
2.JVM内存管理及垃圾回收机制
3.下面分别对这几部分进行说明:
- Java代码编译和执行的整个过程
也正如前面所说,Java代码的编译和执行的整个过程大概是:开发人员编写Java代码(.java文件),然后将之编译成字节码(.class文件),再然后字节码被装入内存,一旦字节码进入虚拟机,它就会被解释器解释执行,或者是被即时代码发生器有选择的转换成机器码执行。
Java虚拟机JVM的内存空间分为五个部分,分别是:
- 1.程序计数器
- 2.Java虚拟机栈
- 3.本地方法栈
- 4.堆
- 5.方法区(1.8中由元空间替代)
1、程序计数器:(线程私有)
每个线程拥有一个程序计数器,在线程创建时创建,
指向下一条指令的地址
执行本地方法时,其值为undefined
说的通俗一点,我们知道,Java是支持多线程的,程序先去执行A线程,执行到一半,然后就去执行B线程,然后又跑回来接着执行A线程,那程序是怎么记住A线程已经执行到哪里了呢?这就需要程序计数器了。因此,为了线程切换后能够恢复到正确的执行位置,每条线程都有一个独立的程序计数器,这块儿属于“线程私有”的内存。
2、Java虚拟机栈:(线程私有)
每个方法被调用的时候都会创建一个栈帧,用于存储局部变量表、操作栈、动态链接、方法出口等信息。局部变量表存放的是:编译期可知的基本数据类型、对象引用类型。
每个方法被调用直到执行完成的过程,就对应着一个栈帧在虚拟机中从入栈到出栈的过程。
在Java虚拟机规范中,对这个区域规定了两种异常情况:
(1)如果线程请求的栈深度太深,超出了虚拟机所允许的深度,就会出现StackOverFlowError(比如无限递归。因为每一层栈帧都占用一定空间,而 Xss 规定了栈的最大空间,超出这个值就会报错)
(2)虚拟机栈可以动态扩展,如果扩展到无法申请足够的内存空间,会出现OOM
3、本地方法栈:
(1)本地方法栈与java虚拟机栈作用非常类似,其区别是:java虚拟机栈是为虚拟机执行java方法服务的,而本地方法栈则为虚拟机执使用到的Native方法服务。
(2)Java虚拟机没有对本地方法栈的使用和数据结构做强制规定,Sun HotSpot虚拟机就把java虚拟机栈和本地方法栈合二为一。
(3)本地方法栈也会抛出StackOverFlowError和OutOfMemoryError。
4、Java堆:即堆内存(线程共享)
(1)堆是java虚拟机所管理的内存区域中最大的一块,java堆是被所有线程共享的内存区域,在java虚拟机启动时创建,堆内存的唯一目的就是存放对象实例几乎所有的对象实例都在堆内存分配。
(2)堆是GC管理的主要区域,从垃圾回收的角度看,由于现在的垃圾收集器都是采用的分代收集算法,因此java堆还可以初步细分为新生代和老年代。
(3)Java虚拟机规定,堆可以处于物理上不连续的内存空间中,只要逻辑上连续的即可。在实现上既可以是固定的,也可以是可动态扩展的。如果在堆内存没有完成实例分配,并且堆大小也无法扩展,就会抛出OutOfMemoryError异常。
5、方法区:(线程共享)
(1)用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
(2)Sun HotSpot虚拟机把方法区叫做永久代(Permanent Generation),方法区中最终要的部分是运行时常量池。