【Java基础】jvm 堆、栈、方法区 & java 内存模型

简介: 在进入主题前,我们先了解一些相关的知识,方面后面对运行时数据区进行分类。进程中,有很多数据是多线程之间共享的,线程在执行时,会先从主存中读取数据,然后复制一份到高速缓存中,当计算完后,再刷新到主存中。

一、 概览

在进入主题前,我们先了解一些相关的知识,方面后面对运行时数据区进行分类。

进程中,有很多数据是多线程之间共享的,线程在执行时,会先从主存中读取数据,然后复制一份到高速缓存中,当计算完后,再刷新到主存中。

我们只要找到独属于线程的资源,那么其他的资源都是线程共享的,线程运行的本质就是函数的执行,函数运行时的信息保存在栈帧中,栈帧中保存了函数的返回值、调用其它函数的参数、java方法、局部变量、操作数栈、动态链接、方法出口、寄存器信息等。除了这个栈帧里面的信息,其他信息都是共享的,据此,我们先画个图,运行时数据区(Runtime Data Area)大致可以分成以下几个区域
在这里插入图片描述

Java运行时数据区(Runtime Data Area)是指在Java程序执行期间,Java虚拟机所管理的诸多内存区域(分别用于存储不同的数据),如上图所示,包含了以下几个部分:

  1. 堆区
  2. 栈区
  3. 方法区
  4. 程序计数器

二、堆区

该区域是一个共享区,主要用于存储对象实例、数组.

Java堆是虚拟机管理的内存中最大的一块区域,jvm只有一个堆区,在虚拟机启动时创建,被所有线程共享,堆是gc管理堆主要区域。

Jvm堆分类

jvm堆一般分为三部分: 新生代,老年代,永久代(元空间)

永久代java8已经被元空间取代。

2.1 新生代

用来存放新生的对象,占据堆的1/3空间

如果新创建的对象占用内存很大,则直接分配到老年代。(当老年代也满了装不下的时候,就会抛出OOM异常。)

2.2 老年代

老年代的对象比较稳定,所以MajorGC不会频繁执行。

在进行MajorGC前一般都先进行了一次MinorGC, 使得有新生代的对象晋身入老年代,导致空间不够用时才触发。 当无法找到足够大的连续空间分配给新创建的较大对象时 也会提前触发一次MajorGC进行垃圾回收腾出空间。

2.3 永久代(元空间)

指内存的永久保存区域,主要存放Class和Meta(元数据)的信息。

Class在被加载的时候被放入永久区域。类的元数据放入 native memory, 字符串池和类的静态变量放入java堆中。

永久代(元空间)和和存放实例的区域不同,GC不会在主程序运行期对永久区域进行清理。所以这也导致了永久代的区域会随着加载的Class的增多而胀满,最终抛出OOM异常。

在Java8中,永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代。

元空间的本质和永久代类似,都是对JVM规范中方法区的实现。

元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。

这样可以加载多少类的元数据就不再由MaxPermSize控制,而由系统的实际可用空间来控制。

三、栈

虚拟机栈:java方法、局部变量、操作数栈、动态链接、方法出口等。

本地方法栈:native方法

栈帧

栈帧也叫过程活动记录,是编译器用来实现过程/函数调用的一种数据结构。简言之,栈帧就是利用EBP(栈帧指针,请注意不是ESP)寄存器访问局部变量、参数、函数返回地址等的手段。

关于本地方法栈和Java栈,在Java虚拟机规范中定义了两种异常。

线程的请求栈的深度大于虚拟机所允许的深度,将抛出StackOverflowError异常
虚拟机在扩展栈时无法申请到足够的内存时,将抛出OutOfMemoryError异常
每次函数调用都会生成对应的栈帧,从而占用一定的内存

四、方法区

线程共享的内存区域,主要用于存储类型信息、常量、静态变量、即时编译代码等

五、程序计数器

CPU在执行程序时,需要有一个地方存放下一条要被取走指令的位置,这是一个寄存器,cpu中只有一个程序计数器。
虚拟机字节码指令的地址或undefined

每个线程都有私有(独立)的程序计数器。

线程中的程序计数器可以理解为一段内存,用来保存当前线程执行到的位置,因为系统采用时间片轮转的方法,所以一个线程不可能一直占用CPU,只能执行规定时间,进行线程切换,这里就需要有一个私有的线程计数器,也就是本地计数器,来保存当前线程的执行到的位置,等到下一次再从这个位置继续执行。

六、Java内存模型(Java Memory Model,JMM)

在多线程场景下,CPU会出现缓存一致性问题,处理器重新排序问题,

为了解决这个问题,制定了计算机内存模型。(原子性、可见性、有序性)
即是Java语言对这个操作规范的遵循,

JMM规定了所有的变量都存储在主存中,每个线程都有自己的工作区,线程将使用到的变量从主存中复制一份到自己的工作区,线程对变量的所有操作(读取、赋值等)都必须在工作区,不同的线程也无法直接访问对方工作区,线程之间的消息传递都需要通过主存来完成。可以把这里主存类比成计算机内存模型中的主存,工作区类比成计算机内存模型中的高速缓存。

相关文章
|
10月前
|
安全 Oracle Java
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡
703 0
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡
|
11月前
|
存储 运维 Kubernetes
Java启动参数JVM_OPTS="-Xms512m -Xmx1024m -XX:+HeapDumpOnOutOfMemoryError"
本文介绍了Java虚拟机(JVM)常用启动参数配置,包括设置初始堆内存(-Xms512m)、最大堆内存(-Xmx1024m)及内存溢出时生成堆转储文件(-XX:+HeapDumpOnOutOfMemoryError),用于性能调优与故障排查。
1211 0
|
Arthas 存储 算法
深入理解JVM,包含字节码文件,内存结构,垃圾回收,类的声明周期,类加载器
JVM全称是Java Virtual Machine-Java虚拟机JVM作用:本质上是一个运行在计算机上的程序,职责是运行Java字节码文件,编译为机器码交由计算机运行类的生命周期概述:类的生命周期描述了一个类加载,使用,卸载的整个过类的生命周期阶段:类的声明周期主要分为五个阶段:加载->连接->初始化->使用->卸载,其中连接中分为三个小阶段验证->准备->解析类加载器的定义:JVM提供类加载器给Java程序去获取类和接口字节码数据类加载器的作用:类加载器接受字节码文件。
1033 55
|
Arthas 监控 Java
Arthas memory(查看 JVM 内存信息)
Arthas memory(查看 JVM 内存信息)
1042 6
|
8月前
|
存储 缓存 Java
我们来说一说 JVM 的内存模型
我是小假 期待与你的下一次相遇 ~
557 5
|
8月前
|
存储 缓存 算法
深入理解JVM《JVM内存区域详解 - 世界的基石》
Java代码从编译到执行需经javac编译为.class字节码,再由JVM加载运行。JVM内存分为线程私有(程序计数器、虚拟机栈、本地方法栈)和线程共享(堆、方法区)区域,其中堆是GC主战场,方法区在JDK 8+演变为使用本地内存的元空间,直接内存则用于提升NIO性能,但可能引发OOM。
|
缓存 Prometheus 监控
Elasticsearch集群JVM调优设置合适的堆内存大小
Elasticsearch集群JVM调优设置合适的堆内存大小
2774 1
|
存储 设计模式 监控
快速定位并优化CPU 与 JVM 内存性能瓶颈
本文介绍了 Java 应用常见的 CPU & JVM 内存热点原因及优化思路。
1357 166
|
存储 缓存 算法
JVM简介—1.Java内存区域
本文详细介绍了Java虚拟机运行时数据区的各个方面,包括其定义、类型(如程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区和直接内存)及其作用。文中还探讨了各版本内存区域的变化、直接内存的使用、从线程角度分析Java内存区域、堆与栈的区别、对象创建步骤、对象内存布局及访问定位,并通过实例说明了常见内存溢出问题的原因和表现形式。这些内容帮助开发者深入理解Java内存管理机制,优化应用程序性能并解决潜在的内存问题。
771 29
JVM简介—1.Java内存区域
|
缓存 监控 算法
JVM简介—2.垃圾回收器和内存分配策略
本文介绍了Java垃圾回收机制的多个方面,包括垃圾回收概述、对象存活判断、引用类型介绍、垃圾收集算法、垃圾收集器设计、具体垃圾回收器详情、Stop The World现象、内存分配与回收策略、新生代配置演示、内存泄漏和溢出问题以及JDK提供的相关工具。
JVM简介—2.垃圾回收器和内存分配策略