欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

认识JVM的内存分配

程序员文章站 2022-07-05 11:30:33
当我们在JVM中运行一段程序代码,JVM初始运行的时候都会分配好 ,而JVM每遇到一个线程,就为其分配一个Program Counter Register(程序计数器), VM Stack(虚拟机栈)和Native Method Stack (本地方法栈),当线程终止时,三者(虚拟机栈,本地方法栈和 ......

当我们在jvm中运行一段程序代码,jvm初始运行的时候都会分配好method area(方法区)和heap(堆),而jvm每遇到一个线程,就为其分配一个program counter register(程序计数器), vm stack(虚拟机栈)和native method stack (本地方法栈),当线程终止时,三者(虚拟机栈,本地方法栈和程序计数器)所占用的内存空间也会被释放掉。

这也是为什么我把内存区域分为线程共享和非线程共享的原因,非线程共享的那三个区域的生命周期与所属线程相同,而线程共享的区域与java程序运行的生命周期相同,所以这也是系统垃圾回收的场所只发生在线程共享的区域的原因。

graph lr a(申请新内存)-->b(eden区) b--内存充足-->c(在eden直接分配) b--第一次内存不足-->d(首次进行yong gc,ygc) d--eden内存活的对象复制到s1-->e(survivor1,s1) b--第二次内存不足-->f(再次进行ycc) f--eden+s1复制到s2清理eden和s1-->ff(survivor2,s2) f--生存次数超过阈值的对象-->g(老年代) g--老年代空间不足时-->h(进行full gc,fgc)

no.1 程序计数器

程序计数器是一块较小的内存区域,作用可以看做是当前线程执行的字节码的位置指示器。分支、循环、跳转、异常处理和线程恢复等基础功能都需要依赖这个计算器来完成。

no.2 虚拟机栈

虚拟机栈也叫栈内存,是在线程创建时创建,它的生命期是跟随线程的生命期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束,该栈就释放了,所以不存在垃圾回收。 它所描述的是java方法执行的内存模型,每个方法执行的同时创建帧栈(strack frame)用于存储局部变量表(包含了对应的方法参数和局部变量),操作栈(operand stack,记录出栈、入栈的操作),动态链接、方法出口等信息,每个方法被调用直到执行完毕的过程,对应这帧栈在虚拟机栈的入栈和出栈的过程。

2.1、局部变量表

局部变量表存放了编译期可知的各种: 基本数据类型(boolean、byte、char、short、int、float、long、double) 对象的引用(reference类型,不等同于对象本身,根据不同的虚拟机实现,可能是一个指向对象起始地址的引用指针,也可能是一个代表对象的句柄或者其他与对象相关的位置) returnadress类型(指向下一条字节码指令的地址) 局部变量表所需的内存空间:在编译期间分配,运行期间不改变(大小固定)。

2.2、栈帧

栈帧是一个内存区块,是一个数据集,是一个有关方法(method)和运行期数据的数据集,当一个方法 a 被调用时就产生了一个栈帧 f1,并被压入到栈中,a 方法又调用了 b 方法,于是产生栈帧 f2 也被压入栈,执行完毕后,先弹出 f2栈帧,再弹出 f1 栈帧,遵循“先进后出”原则。

no.3 堆

heap(堆)是jvm的内存数据区。该区域是被所有线程共享的内存区域,在jvm启动时候创建,专门用来保存对象的实例。 在heap中分配一定的内存来保存对象实例,实际上也只是保存对象实例的属性值,属性的类型和对象本身的类型标记等,并不保存对象的方法(以帧栈的形式保存在stack中)。而对象实例在heap中分配好以后,需要在stack中保存一个4字节的heap内存地址,用来定位该对象实例在heap中的位置,便于找到该对象实例。 堆是线程共享的内存区域,在jvm启动时候创建,专门用来保存对象的实例。 java堆处于物理不连续的内存空间中,只要逻辑上连续即可。 垃圾回收的主要场所。

no.4 方法区

方法区存放了:

  • 加载类的类定义数据
  • 常量和静态变量
  • jit(即时编译器)编译后的代码

方法区也可以是内存不连续的区域组成的,并且可设置为固定大小,也可以设置为可扩展的 垃圾回收在这个区域会比较少出现,这个区域内存回收的目的主要针对常量池的回收和类的卸载。

no.5 运行时常量池(runtime constant pool)

方法区内部有一个非常重要的区域,叫做运行时常量池(runtime constant pool,简称 rcp)。 在字节码文件(class文件)中,除了有类的版本、字段、方法、接口等相关信息描述外,还有常量池(constant pool table)信息,用于存储编译器产生的字面量和符号引用。这部分内容在类被加载后,都会存储到方法区中的rcp。值得注意的是,运行时产生的新常量也可以被放入常量池中,比如 string 类中的 intern() 方法产生的常量。 常量池就是这个类型用到的常量的一个有序集合。包括直接常量(基本类型,string)和对其他类型、方法、字段的符号引用.例如:

  • 类和接口的全限定名;
  • 字段的名称和描述符;
  • 方法和名称和描述符。

池中的数据和数组一样通过索引访问。由于常量池包含了一个类型所有的对其他类型、方法、字段的符号引用,所以常量池在java的动态链接中起了核心作用.

no.6 本地方法栈

与vm strack相似,vm strack为jvm提供执行java方法的服务,native method stack则为jvm提供使用native 方法的服务。

no.7 直接内存区

直接内存区并不是jvm 管理的内存区域的一部分,而是其之外的。该区域也会在 java 开发中使用到,并且存在导致内存溢出的隐患。如果你对 nio 有所了解,可能会知道 nio 是可以使用 native methods 来使用直接内存区的。

jvm内存区域可以分为线程共享和非线程共享两部分,线程共享的有堆和方法区非线程共享的有虚拟机栈,本地方法栈和程序计数器