复习一下JVM内存结构

这篇具有很好参考价值的文章主要介绍了复习一下JVM内存结构。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、程序计数器

程序计数器内存很小,可以看作是当前线程所执行字节码的行号指示器

有了它,程序就能被正确的执行。

因为有线程切换的存在,则每个线程必须有各自独立的程序计数器,即线程私有的内存。

这里再解释一下什么是线程切换,线程切换指的是:

单处理器在执行多线程时所进行的线程切换,多线程的交替运行会产生同时运行的错觉。

程序计数器不会发生OOM原因:

占用内存非常小,当线程结束时程序计数器也会随之回收。

二、本地方法栈与虚拟机栈

栈是stack的翻译,那stack又是什么?

在英文语境中,stack指的是一摞盘子堆叠起来、一摞书堆叠起来的这种状态,也就是 a stack of books. 借这种现实物理情境来描述计算机中的数据结构。

这种结构的特征就是LIFO, Last In First Out, 即后进先出

也就是,一摞盘子,你只能一个一个往上堆,也只能一个一个从顶上往外取,对应**入栈和出栈(弹栈)**的操作。

以上是对栈这种结构的解释。

接下来说这两种栈结构:Native Method Stack 和 JVM stack.

栈是线程私有的,它的生命周期和线程是相同的。

栈里面保存栈帧

什么是栈帧

每个方法执行时都会创建一个栈帧。栈帧存储了局部变量表、操作数栈、动态连接和方法出口等信息。每个方法从调用到运行结束的过程,就对应着一个栈帧在栈中入栈到出栈的过程

栈有可能出现什么异常?

StackOverflowErrorOutOfMemoryError

前者主要是深度递归和复杂嵌套方法调用造成。

后者的话发生在栈在进行动态扩展的时候,也就是说 jvm 实现中栈的大小此时是不固定的,因为线程操作需要更多的栈空间而在申请内存的时候失败就会抛出OutOfMemoryError错误。

JVM 中的栈包括 Java 虚拟机栈和本地方法栈。

两者的区别就是:

Java 虚拟机栈为 JVM 执行 Java 方法服务,本地方法栈则为 JVM 使用到的 Native 方法(比如C或C++)服务。

四、堆

Heap有什么特征?

  • • JVM管理的最大内存区域

  • • 线程共享

  • • 存放对象实例

  • • 内存空间可以物理上不连续

  • • 垃圾回收的主要区域

关于垃圾回收的内容这里不展开讲了。

五、方法区

JVM规范把方法区描述为堆的一个逻辑部分,但有一个别名叫Non-Heap,即Heap中的Non-heap, 也是说明它和堆其实还是不一样的。

方法区有什么特征?

• 线程共享

• 存放已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据

• 垃圾回收较少出现,甚至可选择不进行垃圾回收

方法区的垃圾回收主要针对常量池的回收和对类的卸载

这里主要说一下运行时常量池:

Class文件中除了有类的版本、字段、方法、接口等描述信息外还有一项常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。

这里出现了两个常量池,它们是不一样的,一个叫常量池,一个叫运行时常量池(Runtime Constant Pool), 运行时常量池具备动态性,那怎么理解动态性呢?

就是说除了编译期产生的常量进入了常量池在类加载后又紧接着进入了运行时常量池以外,运行期间新的常量也会进入运行时常量池。

关于类加载的介绍我们再单独写一篇。

《学一点关于JVM类加载的知识》

下面举一些例子把这里具体搞搞清楚。

常量池有什么用 ?

**优点:**常量池避免了频繁的创建和销毁对象而影响系统性能,其实现了对象的共享。

下面具体讲一下字符串常量池(String常量池):

String 是由 final 修饰的类,是不可以被继承的。通常有两种方式来创建对象。

//1、这种存在Heap中,每次new都会创建一个全新对象
String str = new String("abcd");
 
//2、这种是在栈上创建对象引用变量str,指向字符串常量池中的“abcd”(没有的话新建一个)
String str = "abcd";

关于字符串 + 号连接问题:

对于字符串常量的 + 号连接,在程序编译期,JVM就会将其优化为 + 号连接后的值。所以在编译期其字符串常量的值就确定了。

String a = "a1";   
String b = "a" + 1;   
System.out.println((a == b)); //result = true  
 
String a = "atrue";   
String b = "a" + "true";   
System.out.println((a == b)); //result = true 
 
String a = "a3.4";   
String b = "a" + 3.4;   
System.out.println((a == b)); //result = true 

关于字符串引用 + 号连接问题:

对于字符串引用的 + 号连接问题,由于字符串引用在编译期是无法确定下来的,在程序的运行期动态分配并创建新的地址存储对象

    public static void main(String[] args){
           String str1 = "a";
           String str2 = "ab";
           String str3 = str1 + "b";
           System.out.print(str2 == str3);//false
        }

通过 jad 反编译工具,分析上述代码到底做了什么。

    public class TestDemo{
        public TestDemo(){
        }
        public static void main(String args[]){
            String s = "a";
            String s1 = "ab";
            String s2 = (new StringBuilder()).append(s).append("b").toString();
            System.out.print(s1 = s2);
        }
    }

发现 new 了一个 StringBuilder 对象,然后使用 append 方法优化了 + 操作符。new 在上创建对象,而 String s1=“ab”则是在常量池中创建对象,两个应用所指向的内存地址是不同的,所以 s1 == s2 结果为 false。

这里引出一个实际开发中关于字符串拼接的问题。就是尽量不要在 for 循环中使用 + 号来操作字符串

因为如果用“+”号的话,每次循环都会创建和销毁一个StringBuilder对象,这样还不如在循环外创建一个StringBuilder对象,然后使用append方法。

    public static void main(String[] args){
            StringBuilder s = new StringBuilder();
            for(int i = 0; i < 100; i++){
                s.append("a");
            }
        }

使用final修饰的字符串

    public static void main(String[] args){
            final String str1 = "a";
            String str2 = "ab";
            String str3 = str1 + "b";
            System.out.print(str2 == str3);//true
        }

final 修饰的变量是一个常量,编译期就能确定其值。所以 str1 + "b"就等同于 "a" + "b",所以结果是 true。

String对象的intern方法。

    public static void main(String[] args){
            String s = "ab";
            String s1 = "a";
            String s2 = "b";
            String s3 = s1 + s2;
            System.out.println(s3 == s);//false
            System.out.println(s3.intern() == s);//true
        }

通过前面学习我们知道,s1+s2 实际上在堆上 new 了一个 StringBuilder 对象,而 s 在常量池中创建对象 “ab”,所以 s3 == s 为 false。

但是 s3 调用 intern 方法,返回的是s3的内容(ab)在常量池中的地址值。所以 s3.intern() == s 结果为 true。


往期推荐:

● 师爷,翻译翻译什么叫AOP

● 终于搞懂动态代理了!

● 学会@ConfigurationProperties月薪过三千

● 0.o?让我看看怎么个事儿之SpringBoot自动配置

● 不是银趴~是@Import!

● Java反射,看完就会用文章来源地址https://www.toymoban.com/news/detail-829300.html

到了这里,关于复习一下JVM内存结构的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • go限流、计数器固定窗口算法/计数器滑动窗口算法

    问题1:后端接口只能支撑每10秒1w个请求,要怎么来保护它呢? 问题2:发短信的接口,不超过100次/时,1000次/24小时,要怎么实现? 所谓固定窗口,就是只设置了一个时间段,给这个时间段加上一个计数器。 常见的就是统计每秒钟的请求量。 这里就是一个QPS计数器。 在这一

    2024年04月26日
    浏览(39)
  • FPGA拾忆_(3):调用IP 计数器&BCD计数器

    调用IP计数器: 每来一个cin(进位输入)信号,计数器输出值加一,当计数值为9且cin为1时,输出一个时钟长度的cout(进位输出)信号。 首先采用调用quartus种IP的方式,具体步骤: Tools----IP Catalog: 然后会调出IP目录窗口: 通过搜索counter来添加计数器模块,需要设置的内容

    2024年02月03日
    浏览(55)
  • 【期末不挂科-单片机考前速过系列P6】(第六章:10题速过定时计数器的结构和工作方式例题)经典例题盘点(带图解析)

    前言 大家好吖,欢迎来到 YY 滴单片机系列 ,热烈欢迎! 本章主要内容面向接触过单片机的老铁 主要内容含: 欢迎订阅 YY 滴C++专栏!更多干货持续更新!以下是传送门! YY的《C++》专栏 YY的《C++11》专栏 YY的《Linux》专栏 YY的《数据结构》专栏 YY的《C语言基础》专栏 YY的《

    2024年02月02日
    浏览(51)
  • verilog手撕代码5——计数器(置位、加减、环形、扭环形、格雷码计数器实现)

    2023.5.12 编写一个十六进制计数器模块,计数器输出信号递增每次到达0,给出指示信号 zero ,当置位信号 set 有效时,将当前输出置为输入的数值 set_num 。 注意 :这里zero=1和num=0是同一拍输出的,按道理如果根据num=0,然后去输出zero=1应该延迟一拍。所以这里考虑将number延迟一

    2024年02月07日
    浏览(52)
  • LR中监控ORACLE数据库常用计数器(如何自定义Oracle计数器)

    目录 一、添加自定义计数器的方法 1、要创建自定义查询,请执行以下操作: 2、配置文件示例对象 二、常用自定义计数器列表 三、LR中监控ORACLE数据库常用计数器遇到问题及处理 1. 在安装路径的Mercury LoadRunnerdatmonitors找到vmon.cfg文件,打开。 2. 在vmon.cfg文件的第三行中,

    2024年02月15日
    浏览(51)
  • 【FPGA】Verilog:时序电路设计 | 二进制计数器 | 计数器 | 分频器 | 时序约束

    前言: 本章内容主要是演示Vivado下利用Verilog语言进行电路设计、仿真、综合和下载 示例:计数器与分频器   ​​ 功能特性: 采用 Xilinx Artix-7 XC7A35T芯片  配置方式:USB-JTAG/SPI Flash 高达100MHz 的内部时钟速度  存储器:2Mbit SRAM   N25Q064A SPI Flash(样图旧款为N25Q032A) 通用

    2024年02月02日
    浏览(58)
  • 用74LS73设计四位二进制加法计数器和8421BCD加法计数器

     (1)用2片74LS73实现该电路,由CP端输入单脉冲,设计并画出4位异步二进制加法计数器电路图。  (2)由CP端输入单脉冲,测试并记录Q1~Q4端状态及波形。 四位二进制加法计数器状态迁移表如下: Q 4n Q 3n Q 2n Q 1n Q 4n+1 Q 3n+1 Q 2n+1 Q 1n+1 0 0 0 0 0 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 1 1 0 0 1 1 0 1 0

    2024年02月10日
    浏览(87)
  • 【期末不挂科-考前速过系列P6】单片机[接口与总线]——经典例题盘点(带图解析)(第六章:10题速过定时计数器的结构和工作方式例题)

    前言 大家好吖,欢迎来到 YY 滴单片机系列 ,热烈欢迎! 本章主要内容面向接触过单片机的老铁 主要内容含: 欢迎订阅 YY 滴C++专栏!更多干货持续更新!以下是传送门! YY的《C++》专栏 YY的《C++11》专栏 YY的《Linux》专栏 YY的《数据结构》专栏 YY的《C语言基础》专栏 YY的《

    2024年02月03日
    浏览(50)
  • 定时器/计数器中定时/计数初值的计算

             寄存器TMOD是单片机的一个特殊功能寄存器,其功能是控制定时器/计数器T0、T1的工作方式。它的字节地址为89H, 不可以对它进行位操作。          只能进行字节操作, 即给寄存器整体赋值的方法 设置初始值 ,如 TMOD=0x01 。在上电和复位时,寄存器TMOD的初始

    2024年02月10日
    浏览(48)
  • 数电:计数器

    同步计数器和异步计数器的区别 1.同步计数器的外部时钟端都连在一起,而异步计数器没有 2.同步计数器在外部信号到来时触发器同时翻转,而异步计数器的触发器为串行连接,工作频率较低 3.异步计数器输出状态的建立,要比CP慢一个传输时间,容易存在竞争冒险 竞争冒险

    2024年02月06日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包