JVM之【运行时数据区】
目录
前言
线程私有区域
程序计数器
虚拟机栈
概述
特点
存在的异常
本地方法栈
线程共享区域
堆空间
概述
内存划分
JDK7及以前
JDK8
JDK9
本地内存
元空间
直接内存
相关的JVM参数
前言
JVM运行Java程序时,会把自己管理的内存分为多个不同的数据区域,这些区域分为线程私有的内存区域和线程共享的内存区域
线程私有区域
创建每条线程,JVM都会为线程分配的独立的区域,这些区域的生命周期和线程一致,这些区域对其他线程是不可见的,只有当前线程可以访问;线程私有的内存区域有程序计数器、虚拟机栈、本地方法栈
程序计数器
是JVM所有内存区域中,唯一不会发生内存溢出的区域,核心作用如下
- 记录线程正在执行的字节码指令地址,执行引擎处理完某条指令后,程序计数器需要更新将指针指向下一条要执行的指令地址,执行引擎会根据程序计数器记录的地址来执行对应的指令
- 保证线程发生CPU时间片切换后能恢复到正确的位置执行
虚拟机栈
概述
又叫Java栈,主要作用是负责程序如何执行,如何处理数据;当线程执行一个方法时,会为方法生成一个栈帧,方法的调用对应着虚拟机栈中一个栈帧的入栈到出栈的过程;栈帧主要包含局部变量表、操作数栈、动态链接、方法出口等;执行引擎只会执行位于栈顶的栈帧,也就是当前方法
特点
- 操作数栈的栈顶数据会放到高速缓存或者寄存器中,访问速度快
- 虚拟机栈区域中不存在垃圾收集,但是存在内存溢出
- 每条线程的虚拟机栈默认大小为1M
存在的异常
- StackOverflowError:线程请求的栈深度过深,大于虚拟机所允许的最大深度
- OutOfMemoryError:扩容时无法获取到足够的内存空间
本地方法栈
用于执行由C语言编写的本地方法,本地方法的执行是在操作系统中执行的,并不是在JVM中执行,所以使用的是操作系统的程序计数器而不是JVM的程序计数器,可以访问JVM的任何内存区域,也可以直接使用CPU的寄存器和本地内存
线程共享区域
Java程序在运行时,这些区域是程序中所有的线程都可见的,创建后会和JVM的生命周期一致,线程共享的内存区域有堆空间、本地内存
堆空间
概述
Java堆是用来存储数据的,主要解决的是数据的存放问题;堆空间是JVM启动时创建出来的内存区域,默认情况下不通过参数设置堆的大小的话,堆空间的起始大小为当前物理机内存的1/64,最大大小为当前物理机内存的1/4;在程序执行过程中,产生的大部分实例对象都会被放到堆空间存储
内存划分
Java堆空间的内存划分变化的比较频繁,不同的Java版本中,堆空间的内存划分会发生变化,从本质上来讲影响内存结构变化的原因是JVM运行时使用的垃圾收集器
JDK7及以前
堆空间被划分为三个区域:新生代、老年代、永久代
- 新生代:eden区+s0区+s1区,理论比例8:1:1(实际6:1:1,JVM有自适应调节机制)
- 老年代:old区,和新生代的比例为2:1,存储年龄达标的对象和大对象(超过设定的阈值大小)
- 永久代:方法区,存储类的元数据信息
JDK8
将永久代整合成元数据空间,并放到堆空间之外的本地内存中
- 新生代
- 老年代
JDK9
默认垃圾收集器由原来的分代变成G1,有了分区的概念并保留了新生代和老年代的概念,G1将堆空间划分为若干大小相等的Region区,体现了逻辑分代物理分区的思想
- 代表eden的Region区
- 代表survivor的Region区
- 代表old的Region区
- 代表humongous的Region区,存放大对象(G1中不会让大对象进入老年代),判断大对象的方式是对象大小超过了Region区大小的一半
本地内存
主要可分为两部分:元空间和直接内存
元空间
主要用于存放运行时常量池和类信息
- 运行时常量池:final常量值、基本类型数值、符号引用
- 类信息:属性、方法、类元信息(通过反射能获取到的元数据信息)
直接内存
不是虚拟机的内存区域,在创建时直接向操作系统申请内存,直接使用物理内存,和堆空间相比,访问直接内存中的数据速度更快
相关的JVM参数
- [-Xss]:设置虚拟机栈的大小
- [-XX:NewRatio]:指定新生代所占比例
- [-Xmn]:强制指定新生代的内存最大大小
- [-XX:SurvivorRatio]:设置eden区和survivor区的比例
- [-XX:+UseAdaptiveSizePolicy]:开启JVM自适应机制(默认开启)
- [-Xms]:指定堆的初始内存大小
- [-Xmx]:指定堆的最大内存大小
- [-XX:G1HeapRegionSize]:垃圾收集器使用的是G1,指定每个Region区的大小,不推荐手动设置,使用默认即可
- [-XX:G1NewSizePercent]:垃圾收集器使用的是G1,设置新生代初始占比,默认占堆内存的5%
- [-XX:G1MaxNewSizePercent]:垃圾收集器使用的是G1,设置新生代最大占比,建议最多不超过60%
- [-XX:MaxDirectMemorySize]:设置直接内存的最大内存大小