JVM-4. 内存模型

JVM 是一个完整的计算机模型,所以自然就需要有对应的内存模型,这个模型被称为 “Java 内存模型”,对应的英文是“Java Memory Model”,简称 JMM

Java 内存模型规定了 JVM 应该如何使用计算机内存(RAM)。 广义来讲, Java 内存模型分为两个部分:

  • JVM 内存结构
  • JMM 与线程规范

其中,JVM 内存结构是底层实现,也是我们理解和认识 JMM 的基础。 大家熟知的堆内存、栈内存等运行时数据区的划分就可以归为 JVM 内存结构。

JVM内存结构

jvm内部使用的Java内存模型,逻辑上分为线程栈和堆内存。

线程栈在这里插入图片描述

每个运行的线程都有自己的线程栈,线程栈包含了当前正在执行方法链/调用链上所有方法的状态信息。所有又称为方法栈或者调用栈。

  1. 每个线程都只能访问自己的线程栈。
  2. 每个线程都不能访问(看不见)其他线程的局部变量。
  3. 所有原生类型的局部变量都存储在线程栈中,因此对其他线程是不可见的。
  4. 线程可以将一个原生变量值的副本传给另一个线程,但不能共享原生局部变量本身。
  5. 堆内存中包含了Java代码中创建的所有对象,不管是那个线程创建的。其中也涵盖了包装类型(例如Byte、Interger、Long等)
  6. 不管是创建一个对象并将其赋值给局部变量,还是赋值给另一个对象的成员变量,创建的对象都会保存到堆内存中。
  7. 如果是原生数据类型的局部变量,那么他的内容就全部保留在线程栈上。
  8. 如果是对象引用,栈中的局部变量保存这对象的引用地址,实际的对象内容保存在堆中。
  9. 对象的成员变量与对象本身一起存储在堆上,不管成员变量的类型是原生数值还是对象引用。
  10. 类的静态变量和类定义一样都保存在堆中。

总结:原始数据类型和对象引用地址在栈上;对象、对象成员与类定义、静态变量在堆上。

堆内存

堆内存又称为"共享堆",堆中所有对象可以被所有线程访问,只要线程能找到(拿到对象的引用实例)

  1. 如果一个线程可以访问某个对象,那就能访问该对象的成员变量
  2. 如果两个线程同时调用某个对象的同一方法,则他们都可以访问到这个对象的成员变量,但是每个线程的局部变量副本是独立的。

总结:各个线程自己使用的局部变量都在自己的栈上,可以共享堆上的对象,不同线程访问同一个对象实例的基础类型的成员变量时,会给每个线程一个变量的副本。

栈内存结构

栈内存结构
Java虚拟机栈是线程私有的内存空间,他和线程一起创建。当一个线程创建时,会在虚拟机栈中申请一个线程栈,用来保存方法的局部变量、操作数栈、动态连接方法和返回地址等信息,并参与方法的调用和返回。每一个方法额调用都伴随着栈帧的入栈操作,方法的返回则为出栈操作。

每启动一个线程,JVM 就会在栈空间栈分配对应的线程栈, 比如 1MB 的空间(-Xss1m)。

线程栈也叫做 Java 方法栈。 如果使用了 JNI 方法,则会分配一个单独的本地方法栈(Native Stack)。

线程执行过程中,一般会有多个方法组成调用栈(Stack Trace), 比如 A 调用 B,B 调用 C……每执行到一个方法,就会创建对应的栈帧(Frame)。

堆结构

堆结构

堆是JVM内存中最大的一块内存空间,内存被所有线程所共享,几乎所有的对象和数组都被分配到堆内存中。

在逻辑上将堆分为堆(Heap)和非堆(Non-Heap)两个部分。我们编写的Java代码基本上只用Heap这部分,发生内存分配和回收的主要部分。

中又包括新生代和老年代,新生代又进一步划分为Eden和Survivor区,Survivor有由From和To Survivor组成。To和From Survivor总有一个是空的。

1.7版本中永久代的静态变量和运行时常量池被合并到堆中。到了1.8中,永久代被元空间取代了。

非堆用来存储每个类结构,如运行时常数池、字段和方法数据,以及方法和构造方法的代码。它是在 Java 虚拟机启动时创建的。方法区(所有线程共享)属于非堆内存。

所以一般我们将xmx的内存设置为容器60%-75%,除了堆还有非堆、jvm自身还需要内存空间。

方法区(Method Area)

用来存放已经被虚拟机加载的类的相关信息,包括类信息、运行时常量池、字符串常量池。类信息又包括类的版本、字段、方法、接口和父类信息。

JVM在执行某个类的执行,必须经过加载、连接、初始化,而连接又包括验证、准备、解析三个阶段。在加载类的时候,JVM会先加载class文件,而在class文件中除了又类的版本、方法和接口等描述信息之外,还有一项信息是常量池,用于存放编译期间生成额各种字面量和符号引用。

字面量包括字符串(String a=“b”)、基本类型的常量(final修饰的变量),符号引用则包括类和方法的全限定名。

当类加载到内存中后,JVM就会将class文件常量池中的内容存放到运行时的常量池中;在解析阶段,JVM会把符号引用替换为直接引用(对象的索引值)

程序计数器(Program Counter Register)

程序计数器是一块很小的内存空间,主要记录各个线程的执行的字节码的地址。例如,分支、循环、跳转、异常、线程恢复等都依赖计数器。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值