浅谈JVM内存结构

本文详细介绍了Java虚拟机(JVM)的内存模型,包括程序计数器、虚拟机栈、本地方法栈、堆内存(包括类静态常量池和字符串常量池)以及元空间。重点讲解了内存分配、线程管理和字符串优化等内容,为理解`==`和`equals()`的行为提供基础。
摘要由CSDN通过智能技术生成


前言

在了解==和equals()过程中,发现结论好记,但是一到运用的时候,有些搞不明白为什么,所以了解一下JVM、常量池、堆栈的知识,并做个笔记。


一、JVM内存模型

1、JVM内存分配图

在这里插入图片描述


2、JVM是什么?

JVM是Java的核心组成部分之一,是一种字节码解释器,相当于一台虚拟机,可以将Java 代码编译成字节码,使Java字节码可以在计算机上运行。因为JVM的存在使得Java程序可以在不同的操作系统和硬件上运行。

JVM主要分为四大模块:类加载系统、垃圾回收器、字节码执行引擎、JVM运行数据区
以下本章主要讲述JVAM运行数据区。


二、JVM内部结构

1、程序计数器

程序计数器属于线程私有,主要有两个作用

  1. 记录着当前执行指令的地址信息
  2. **通过修改程序计数器的值,实现代码的流程控制。如分支、选择、循环、异常处理等

为什么要记录当前执行的指令信息?

Java 虚拟机的多线程处理中,处理器通过轮流切换线程执行,线程执行完成后才会释放资源或者等待的一个过程。既然有线程切换这样一个过程,自然需要有序的入队返队这样的一个概念,所以为了线程切换后能返回原先位置,每条线程都需要有一个独立的程序计数器记录自己的地址信息。


2、虚拟机栈

在这里插入图片描述
1、在JVM中,虚拟机栈属于线程私有,一个线程对应一个虚拟机栈,一个方法对应一个栈帧,调用方法到结束的过程,就是栈帧进栈和出栈的过程
2、每个栈帧中对应一个方法的局部变量表,其存放着方法运行时用到的基本数据类型和各种对象引用类型(不是对象本身,对象都在堆中)
3、异常处理:StackOverflowErrorOutOfMemoryError(栈帧内存大小在编译期就已确定,不会因为程序的运行而改变)

  • 当程序执行所需的栈内存超过设定的固定值会抛出StackOverflowError错误。
  • 当栈内存设置为动态增长的时候,如果申请的内存大小超过了其可用内存时会抛出OutOfMemoryError错误。

这关键点:基本数据类型存储和引用类型存储在栈中,而对象和数组存储在堆中


3、本地方法栈

本地方法栈与虚拟机栈所发挥的作用是非常相似的,其区别是虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则是为虚拟机使用到的本地方法服务。本地方法可以理解为不是Java语言实现的方法,这些本地方法是由其他语言编写,如C\C++。如果当我们需要与操作系统进行数据交互时,就可以通过本地方法实现java程序与操作系统资源交互。


4、堆内存

  • 堆heap属于线程共享,是Java 虚拟机所管理中最大的一块内存区域。几乎所有的对象实例、数组都在这里创建、分配内存
  • 堆中单独分配出了一个区域,作为字符串常量池作用于String操作
  • 垃圾回收器也主要作用于堆heap,有不同的回收算法(这里不多加阐述)

关于堆这块的知识和应用可太多了,我没有深挖,所以这只讲个大概,主要是了解堆存储什么和字符串常量池的存储形式。

这主要记住:几乎对象、数组都在堆内存分配

4.1、class静态常量池

这里主要是了解一下
在这里插入图片描述

class文件中有个静态常量池,class文件进入JVM后,静态常量池数据被加载进入运行时常量池和字符串常量池。
静态常量池是Java的一种常量缓存机制,它其内保存了字面量和符号引用,指的是编译期就确定的、不会改变的数据提前存储在常量池中。
字面量表示的就是数据的值。比如 String s = “as” ,as 就是数据的字面量,同理也还有整数型字面量、字符型字面量等。
符号引用是一个唯一的符号,表示对类、变量、方法的引用。因为JVM对类或者接口引用的变量或者方法不知道在那,所以符号引用来代替(自行了解符号引用到直接引用)

需要记住这句话:编译期就确定的、不会改变的数据存储在常量池中,,如String s ="as",这个s编译期就确定了,所以as就会存储到字符串常量池中。因为String类被final修饰,不可更改

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    private final char value[];
    .......

4.2、字符串常量池

字符串常量池是专门为String类型数据设计的常量池,那为什么要设计一个字符串常量池?

为 了减少在JVM中创建的字符串的数量,如果我们每次创建一个字符串,都去内存创建一个实例,平时字符串应用又特别广,那资源和内存的消耗是可怕的,所以JVM提高性能和减少内存开销,设计了一个字符串常量池,当创建字符串时,JVM会首先检查字符串常量池。如果字符串已经存在池中, 就返回池中的实例引用。如果字符串不在池中,就会实例化一个字符串并放到池中。

如下代码,s、a代表字符串的引用,存储在栈中,但是s在编译期就已经确定了,所以 as 存储到了字符串常量中
而 String a = new String(“as”),使用new关键字创建实例并初始化这个对象,是运行时才确定的,所以需要在堆中创建分配a的空间,并创建一个引用,指向字符串常量池的as地址

        String s = "as";
        String a = new String("as");

在这里插入图片描述

5、元空间

1、在JDK8以后,又被称为元空间,之前叫方法区
2、元空间主要存储编译器编译后类信息、方法信息、常量等
3、运行时常量池主要存储用于存放编译期间生成的各种字面常量和直接引用(符号引用变成直接引用)

总结

以上的内容我主要是想了解堆栈的存储对象区别,为了更加深入了解 == 和equals()的应用原理,很多点我没有讲,比如垃圾回收、字节码执行引擎等,当然,其他模块也有很多没有讲到,需求不一样,我只是讲了个大概,望谅解。

  • 21
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值