JVM内存划分
JVM内存划分
概述
java虚拟机(Java Virtual Machine 简称JVM):是运行所有java程序的抽象计算机,是java语言的运行环境。对于java语言来说,在虚拟机的自动内存管理机制的帮助下,不再需要为每一个new操作去写delete/free代码,不容易出现内存泄漏与内存溢出问题
常见的JVM有:SUN的HotSpot(主流、后被Oracle收购)、IBM的J9 VM、BEA的JRockit(后被Oracle收购)
Java的执行过程
java源代码(.java)会被java编译器编译为字节码文件(.class),然后由JVM中的类加载器加载各个类的字节码文件,加载完毕后,交由JVM执行引擎执行。执行过程中,JVM用一段空间来存储程序运行所需的数据,这段空间成为运行时数据区,又称为JVM内存
运行时数据区(JVM内存)划分
根据Java虚拟机规范,Java虚拟机管理的内存包括以下数据区域:程序计数器、java虚拟机栈、本地方法栈、java堆、方法区
程序计数器(Program Counter Register)
程序计数器又称为PC寄存器,是用来记录程序当前执行的位置,每一个线程都有自己独有的程序计数器,执行引擎中的字节码解析器工作时就是通过改变这个计数器的值来选取下一条字节码命令,分支、循环、跳转、异常等基础功能都依赖这个计数器来完成
- 如果线程正在执行的是一个java方法,计数器记录的是正在执行的虚拟机字节码指令的地址,如果正在执行的是Native方法,则计数器的值为空
- 由于程序计数器中存储的数据所在空间大小不会发送改变,因此程序计数器不会发生内存溢出的现象(OOM)
java虚拟机栈(Java Virtual Machine Stack)
java虚拟机栈又称为java栈,线程私有,生命周期与线程相同。每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息
- 局部变量表:基本类型(long和double占用两个局部变量空间,其余占一个)、对象引用、returnAddress(指向一条字节码指令的地址)
- 操作数栈
- 动态链接:将符合引用转换为直接引用
- 方法出口:方法调用完成,去哪里继续执行
对此区域规定了两种异常:
- 线程请求的栈深度大于虚拟机所允许的深度,抛出StackOverFlowError异常
- 如果java虚拟机栈可以动态扩展,如果扩展时无法申请到足够的内存,抛出OutOfMemoryError异常
本地方法栈(Native Method Stack)
本地方法栈与java虚拟机栈的作用相似,区别是java虚拟机栈为执行java方法服务,本地方法栈为Native方法服务,本地方法栈也会抛出StackOverFlowError异常与OutOfMemoryError异常
- HotSpot将本地方法栈与java虚拟机栈合二为一,统称为栈
java堆(Java Heap)
java堆是java虚拟机管理的内存中最大的一块,此区域被所有线程共享,在虚拟机启动时创建,目的是存放对象实例(所有对象实例与数组都在堆上分配)
java堆是GC主要工作的区域,又称为GC堆,规定了OOM异常
内存回收采用分代收集算法
方法区(Method Area)
方法区是所有线程共享的区域,用于存储已被虚拟机加载的类信息(构造方法、接口定义),常量,静态变量
内存回收主要是针对常量池的回收与类型卸载,规定了OOM异常
线程共享的数据区划分
堆内存分为年轻代与老年代,内存大小比例:年轻代:老年代为1:2
年轻代内存又分为伊甸园区与幸存区,幸存区又分为from区、to区,内存大小比例:伊甸园区:from区:to区为8:1:1
堆内存存放的是对象,垃圾收集器回收的是这里的对象,不同区域的对象根据不同的GC算法回收(分代收集算法),年轻代用轻GC,老年代用重GC
非堆内存即永久代,也称为方法区,存储程序运行时长期存活的对象,如类的元数据,常量,属性等
jdk1.8废弃了永久代,使用元空间取代,元空间存储的对象与永久代相同,区别是元空间使用的是本地内存,不在jvm中
参考资料
深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)