当前位置: 首页 > article >正文

一篇博文了解JVM的各个内存区域


JVM的内存区域可以细分为程序计数器、虚拟机栈、本地方法栈、堆和方法区
在这里插入图片描述

其中,方法区和堆是线程共享的,虚拟机栈、本地方法栈和程序计数器是私有的;

先来说说程序计数器,它也被称为PC寄存器,占据着很小的内存空间,它可以看作是当前线程所执行的字节码行号指示器;

接着就是Java虚拟机栈了,它的生命周期和线程相同,当线程执行一个方法时,会创建一个对应的栈帧,用来存储局部变量表、操作数栈、动态链接等信息,然后栈帧会被压入虚拟机栈中,当方法执行完毕之后,栈帧就会从虚拟机栈中移除;
在这里插入图片描述

提到局部变量表,顺便一问,一个什么都没有的方法,空的参数都没有,那局部变量表里有没有变量呢?

这个问题要看方法是否静态,对于静态方法,因为不需要访问实例对象this,所以在局部变量表中不会有任何变量;

而对于非静态方法,即使是一个完全空的方法,局部变量表中也会有一个用于存储this引用的变量,this引用指向当前实例对象,在方法调用时被隐式传入;

这一点我通过查看方法体中没有任何内容的静态方法和非静态方法编译之后各自的字节码,看到非静态方法中locals参数为1,而静态方法中为0,而非静态方法中的locals=1,就是因为其局部变量表中有一个变量this;静态方法中的locals参数为0,就是表示局部变量表为空,因为静态方法属于类级别方法,不需要this引用,也就没有局部变量;

接着介绍下本地方法栈,本地方法栈和虚拟机栈相似,二者的区别在于虚拟机栈是为JVM执行Java编写的方法服务的,而本地方法栈是为Java调用本地native方法服务的,通常由C或者C++编写的;

顺便说下本地方法栈的运行场景,当Java应用需要与操作系统底层或者硬件交互的时候,会用到本地方法栈,比如调用操作系统的特定功能,比如内存管理,文件操作,系统时间,系统调用等,比如我写Java代码时,如果想要获取系统时间,可以用System.currentTimeMills()这个方法,这个方法就是调用本地方法,来获取操作系统当前时间的;再比如JVM自身的一些底层功能也需要通过本地方法来实现。像Object类中的hashCode()方法,clone()方法等;

上面提到了native方法,就顺带解释下;

native方法是在Java中通过native关键字声明的,用来调用非Java语言,比如C/C++编写的代码,Java可以通过JNI,也就是Java Native Interface与底层系统、硬件设备或者本地库交互;

接着讲下JVM中最大的一块内存区域–堆;

堆是JVM中最大的一块内存区域,它可以被所有线程共享,在JVM启动时创建,主要用来存储new出来的对象;

Java中“几乎”所有的对象都会在堆中分配,为什么说几乎,是因为有些比如Java中的字符串字面量“hello”,他们会被存储在字符串常量池中,是一个特殊的内存区域,位于方法区或者元空间,以及基本类型的局部变量,比如int、boolean等基本类型的局部变量是分配在栈上的,而不是堆中;所以说“几乎”而不是全部对象,堆它除了是JVM中最大的一块内存区域,也是垃圾收集器的目标区域;

从内存回收的角度来看,由于垃圾收集器大部分都是基于分代收集理论设计的,所以堆它又被细分为新生代、老年代、Eden空间、FromSurvivor空间、ToSurvivor空间等。

在这里插入图片描述

随着JIT编译器的发展以及逃逸技术的逐渐成熟,以前常说的“所有的对象都会分配到堆上”这句话越来越不经得起推敲;从JDK7开始,JVM默认就开启了逃逸分析;

这里可以再说下逃逸分析,逃逸就是一个对象如果被其他方法或线程访问,就称为逃逸,逃逸又可以分成方法逃逸和线程逃逸,方法逃逸就是对象被其他方法引用,线程逃逸就是对象被其他线程访问,如果对象没有逃逸,JVM就可以对其进行优化;

逃逸分析的主要目的是确定对象的生命周期和作用域,从而决定是否可以进行优化;

JDK7开始默认开启了逃逸分析,就意味着如果某些方法中的对象引用没有被返回或者没有在方法体外使用,也就是没有逃逸出去,那么对象就可以直接在栈上分配内存;

提到了栈,我也可以顺带说下堆和栈的区别;

  • 堆它是属于线程共享的内存区域,几乎所有new出来的对象都会在堆上分配,生命周期不是由单个方法调用决定的,可以在方法调用结束后继续存在,直到不再被任何变量引用,最后被垃圾收集器回收;
  • 而栈是属于线程私有的内存区域,主要存储局部变量、方法参数、对象引用等,通常是随着方法调用的结束而自动释放,不需要垃圾收集器处理;

此时可以补充下变量在堆栈中的存放位置;

  • 对于局部变量,它是存储在当前方法栈帧中的局部变量中,当方法执行完成后,栈帧被回收,局部变量也会被释放
  • 而对于静态变量来说,它存储在Java虚拟机规范中的方法区中,在Java7中是永久代,在Java8及Java8之后,是元空间

最后,我来讲下方法区吧!

方法区并不是真实存在的,hhhh,它是属于虚拟机规范中的一个逻辑概念,用来存储已经被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码缓存等;

在HotSpot虚拟机中,方法区的实现称为永久代PermGen,但是在Java8以及之后的版本中,已经被元空间Metaspace所替代;


http://www.kler.cn/a/518014.html

相关文章:

  • Arduino Uno 和 1.44 英寸 TFT 屏幕(SPI 接口)初体验
  • 1.24寒假作业
  • 11、性能测试及监控Nginx动静分离配置
  • 数据结构——实验八·学生管理系统
  • WPF常见面试题解答
  • C++:定义点和圆的结构体,点包含坐标x,y,圆包含点和半径,用函数来实现某个圆是否包含原点。
  • 关于opensips的帮助命令的解释
  • 华为OD机试E卷 --构成的正方形数量--24年OD统一考试(Java JS Python C C++)
  • react项目表格内容轮播,DataV-React轮播表的使用
  • 如何在docker中的mysql容器内执行命令与执行SQL文件
  • C语言操作符详解
  • Spring Boot WebMvcConfigurer:定制你的 Web 应用
  • Java Map遍历的六种方式
  • 找树左下角的值
  • Effective C++笔记
  • PPT巧制拉链动画:超逼真效果制作教程
  • 2025年,当前比较火的几个互联网学习路线
  • 【自然语言处理(NLP)】序列数据研究(创建序列数据、简单的MLP模型、预测结果分析)
  • Nginx 安全配置与防护策略
  • Oracle查看数据库表空间使用情况