【JVM】【四】【Java8内存结构图】
一、Java8内存结构图
二、术语解析
2.1、虚拟机内存和本地内存
2.1.1、虚拟机内存
Java虚拟机在执行的时候会把管理的内存分配成不同的区域,这些区域被称为虚拟机内存。
- 受虚拟机内存大小的参数控制,当大小超过参数设置的大小时就会报OOM。
2.1.2、本地内存
对于虚拟机没有直接管理的物理内存,也有一定的利用,这些被利用却不在虚拟机内存数据区的内存,我们称它为本地内存。
- 本地内存不受虚拟机内存参数的限制,只受物理内存容量的限制。
- 如果内存的占用超出物理内存的大小,同样也会报OOM。
2.2、Java堆(Java Heap)
java堆是JVM内存中最大的一块,由所有线程共享,是由垃圾收集器管理的内存区域,主要存放:对象实例(基本类型的数组也算对象)、字符串常量池、静态变量(类变量)、线程分配缓冲区(这个属于线程私有)。
- java堆既可以是固定大小的,也可以是可扩展的(通过参数-Xmx和-Xms设定)。
- 如果堆无法扩展或者无法分配内存时也会报OOM。
2.2.1、对象实例
- 类初始化生成的对象
- 基本数据类型的数组也是对象实例(byte/boolean/char/int/long/double/float/short)。
- 其中,定义在类中的成员变量,即没有static修饰符修饰的变量,随着类的实例产生和销毁,是类实例的一部分;在类初始化的时候,从运行时常量池取出直接引用或者值,与初始化的对象一起放入堆中。
2.2.2、字符串常量池
- 字符串常量池存储的是string对象的直接引用,而不是直接存放的对象,是一张string table。
2.2.3、静态变量
- 静态变量是有static修饰的变量,jdk7时从方法区迁移至堆中。
2.2.4、线程分配缓冲区(Thread Local Allocation Buffer)
- 线程私有,但是不影响java堆的共性。
- 增加线程分配缓冲区是为了提升对象分配时的效率。
2.3、方法区(Method Area)
方法区是规范,元空间是实现。
2.3.1、类元信息(Klass)
- 类元信息在类编译期间放入方法区,里面放置了类的基本信息,包括类的版本、字段、方法、接口以及常量池表(Constant Pool Table)。
- 常量池表存储了类在编译期间生成的字面量、符号引用,这些信息在类加载完后会被解析到运行时常量池中。
2.3.2、运行时常量池(Runtime Constant Pool)
- 运行时常量池主要存放在类加载后被解析的字面量与符号引用,但不止这些。
- 运行时常量池具备动态性,可以添加数据,比较多的使用就是String类的intern()方法。
2.4、直接内存
在jdk1.4中加入了NIO(New Input/Putput)类,引入了一种基于通道(channel)与缓冲区(buffer)的新IO方式,它可以使用native函数直接分配堆外内存,然后通过存储在java堆中的DirectByteBuffer对象作为这块内存的引用进行操作,
这样可以在一些场景下大大提高IO性能,避免了在java堆和native堆来回复制数据。
- 直接内存位于本地内存,不属于JVM内存,但是也会在物理内存耗尽的时候报OOM。
2.5、虚拟机栈(JVM Stacks)
虚拟机栈是线程私有的,随线程生灭。每个方法执行时,都会在虚拟机栈同步生成一个栈帧(stack frame)。每个栈帧都包含:局部变量表、操作数栈、动态连接、方法返回地址。
方法被执行时入栈,执行完后出栈。
- 如果线程请求的栈深度大于虚拟机所规定的栈深度,则会抛出StackOverFlowError即栈溢出
- 如果虚拟机的栈容量可以动态扩展,那么当虚拟机栈申请不到内存时会抛出OutOfMemoryError即OOM内存溢出
2.5.1、局部变量表(Local Variable Table)
局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。局部变量包括java基本数据类型、对象引用和returnAddress类型(它指向了一条字节码指令的地址)。(注:这里指的是方法内的局部变量)
2.5.2、操作数栈
2.5.3、动态连接
2.5.4、方法返回地址
2.6、本地方法栈(Native Method Stacks)
本地方法栈与虚拟机栈的作用是相似的,都会抛出OutOfMemoryError和StackOverFlowError,都是线程私有的。主要的区别在于:
1. 虚拟机栈执行的是java方法
2. 本地方法栈执行的是native方法(什么是Native方法?)
2.7、程序计数器(Program Counter Register)
程序计数器就是当前线程所执行的字节码的行号指示器,通过改变计数器的值,来选取下一行指令,通过他来实现跳转、循环、恢复线程等功能。
- 在任何时刻,一个处理器内核只能运行一个线程,多线程是通过线程轮流切换,分配时间来完成的,这就需要有一个标志来记住每个线程执行到了哪里,这里便需要到了程序计数器。
- 所以,程序计数器是线程私有的,每个线程都已自己的程序计数器。
参考:
https://www.cnblogs.com/webor2006/category/1154466.html?page=5
https://www.cnblogs.com/javastack/p/15153856.html
Java虚拟机规范8
深入理解Java虚拟机(第三版)
本文来自博客园,作者:时光与简,转载请注明原文链接:https://www.cnblogs.com/simpletime/p/16036949.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!