【JVM】内存结构

news2024/11/26 9:43:10

【JVM】内存结构

文章目录

  • 【JVM】内存结构
    • 1. 程序计数器
      • 1.1 定义
      • 1.2 作用
    • 2. 虚拟机栈
      • 2.1 定义
      • 2.2 栈内存溢出
      • 2.3 线程运行诊断
    • 3. 本地方法栈
    • 4. 堆
      • 4.1 定义
      • 4.2 堆内存溢出
      • 4.3 堆内存诊断
    • 5. 方法区
      • 5.1 定义
      • 5.2 组成
      • 5.3 方法区内存溢出
      • 5.4 运行时常量池
      • 5.5 StringTable特性

1. 程序计数器

image-20230221000351943

1.1 定义

程序计数器:Program Counter Register 程序计数器(寄存器)

作用:记住下一条jvm指令的执行地址。

特点:

  • 是线程私有的(每个线程都有自己的程序计数器,切换线程的时候才知道接下来执行那条命令)。
  • 随着线程的创建而创建,随线程销毁而销毁。
  • 不会存在内存溢出。

1.2 作用

0: getstatic #20 					// PrintStream out = System.out;
3: astore_1 						// --
4: aload_1 							// out.println(1);
5: iconst_1 						// --
6: invokevirtual #26	 			// --
9: aload_1 							// out.println(2);
10: iconst_2 						// --
11: invokevirtual #26 				// --
14: aload_1 						// out.println(3);
15: iconst_3 						// --
16: invokevirtual #26	 			// --
19: aload_1 						// out.println(4);
20: iconst_4 						// --
21: invokevirtual #26	 			// --
24: aload_1 						// out.println(5);
25: iconst_5 						// --
26: invokevirtual #26 				// --
29: return		

左侧的是 二进制字节码 ,它是jvm指令;而右侧的是 java源代码

但是这些jvm指令不能直接交给CPU执行,它需要先转成机器码才能交给CPU执行。这就需要使用到 解释器 将jvm指令转翻译成成机器码再交给CPU。

而程序计数器的作用就是记住下一条jvm指令的地址,如果没有程序计数器,jvm就不知道下一条该执行哪条命令。

①假设取出一条指令 xxx ,解释器将它翻译成机器码再交给CPU执行,与此同时,把下一条的指令地址放入程序计数器。

②等上一条指令执行完后,解释器再去程序计数器中取出下一条指令的地址,再执行①。


2. 虚拟机栈

image-20230221002410421

2.1 定义

Java Virtual Machine Statcks (Java虚拟机栈)

  • 每个线程运行所需要的内存,称为虚拟机栈。
  • 每个栈由多个栈帧(Frame)组成,对应每次方法调用时占用的内存。
  • 每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法。

问题辨析:

  1. 垃圾回收是否涉及栈内存?
  2. 栈内存分配越大越好吗?
  3. 方法内的局部变量是否线程安全?
    • 如果方法内局部变量没有逃离方法的作用范围,它是线程安全的。
    • 如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全。

1.答:不涉及。栈帧内存在每一次方法调用完毕之后都会弹出栈,不需要垃圾回收来管理栈内存。垃圾回收是去回收堆内存中的无用对象。

2.答:不是。栈内存越大只不过是能进行更多次的方法调用,而且栈内存分配的越大,所支持的线程数会越少。

3.答:安全。局部变量是线程私有的。


2.2 栈内存溢出

栈内存溢出场景:

  • 栈帧过多导致栈内存溢出
  • 栈帧过大导致栈内存溢出

栈内存大小可通过 -Xss 设置,比如设置 -Xss256k 。栈内存默认大小位1M。


2.3 线程运行诊断

案例1:cpu占用过多

定位:

  1. 用top命令定位哪个进程对cpu的占用过高,得到pid
  2. 使用 ps H -eo pid,tid,%cpu | grep pid 。用ps命令进一步定位到是进程的哪个线程cpu占用过高,得到tid
  3. jstack pid
    • 将tid转化为十六进制,与控制台输出比对,进一步定位到问题代码的源码行号

案例2:程序运行很长时间没有结果

  1. 获得程序的进程id(pid)
  2. 使用 jstack pid 查看信息,发现是死锁
  3. 在输出信息的末尾可以发现问题原因和代码的源码行号

3. 本地方法栈

image-20230221225307835

本地方法:不是由java编写的方法,一般是c或c++代码编写的方法。本地方法运行时使用的内存就是本地方法栈。


4. 堆

4.1 定义

Heap 堆

  • 通过new关键字创建的对象都会使用堆内存

特点:

  • 它是线程共享的,堆中对象都需要考虑线程安全的问题。
  • 有垃圾回收机制。

4.2 堆内存溢出

堆内存大小可以通过 -Xmx 设置,比如 -Xmx8m

一般来说不再被使用的对象就会被垃圾回收,但是如果对象一直创建一直被使用,那么就会导致堆内存溢出。


4.3 堆内存诊断

  1. jps工具
    • 查看当前系统中有哪些java进程 (jps)
  2. jmap工具
    • 查看堆内存占用情况 (jmap -heap pid)
  3. jconsole工具
    • 图形界面,多功能的监测工具,可以连续监测 (jconsole)

案例:

  • 垃圾回收后,内存占用仍然很高。

5. 方法区

image-20230221233613566

方法区是所有java虚拟机线程所共享的,它存储了类结构的相关信息,比如成员变量,方法和构造器代码,以及特殊方法。

方法区在虚拟机启动时创建,方法区在逻辑上是堆的组成部分,具体是不是堆的一部分不同jvm厂商的实现方式不一样。


5.1 定义

JVM规范-方法区定义


5.2 组成

image-20230221234622806


5.3 方法区内存溢出

  • 1.8以前会导致永久代内存溢出
* 演示永久代内存溢出 java.lang.OutOfMemoryError: PermGen space
* -XX:MaxPermSize=8m
  • 1.8之后会导致元空间内存溢出
* 演示元空间内存溢出 java.lang.OutOfMemoryError: Metaspace
* -XX:MaxMetaspaceSize=8m

场景:

  • spring
  • mybatis

5.4 运行时常量池

  • 常量池:就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息。
  • 运行时常量池:常量池是*.class文件中的,当该类被加载,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址。
public class Demo{
    public static void main(String[] args){
        String s1="a";
        String s2="b";
        String s3="ab";
    }
}

常量池中的信息在程序运行时都会被加载到运行时常量池中,这时a,b,c都还是常量池中的符号,还没有变为java字符串对象。只有当运行到程序引用了”它“的那一行,他才会成为字符串对象。

如下图所示:

image-20230222204144416

①当程序运行到 ldc #2 时,就要去找一个 a 符号,找到a符号之后就会把它变成字符串对象。

②变成字符串对象之后,jvm需要准备一个 StringTable [] ,又称字符串常量池或串池,它在数据结构上是哈希表,长度固定,不能扩容。

③此时串池还为空,”a“字符串对象创建后把”a“作为key去 StringTable 中找是否有取值相同的key,如果没有,它就会把”a“放入串池。此时串池中只有一个 [“a”]

④接下来两行代码执行完后,串池已经有了三个字符串对象 StringTable ["a","b","ab"]


在原来的代码中新增一行代码,如下所示:

public class Demo{
    public static void main(String[] args){
        String s1="a";
        String s2="b";
        String s3="ab";
        String s4=s1+s2;	//new StringBuilder().append("a").append("b").toString()  =  new String("ab")
        System.out.println( s3 == s4 );
    }
}

将代码编译之后再反编译,如下所示:

image-20230222205950484

s4的值是通过new关键字创建出来的,它存储在堆中,而s3是串池中的字符串对象,它们的地址不同,所以输出false


继续增加代码:

public class Demo{
    public static void main(String[] args){
        String s1="a";
        String s2="b";
        String s3="ab";
        String s4=s1+s2;	//new StringBuilder().append("a").append("b").toString()  =  new String("ab")
        String s5="a"+"b";	//javac 在编译期间就会将"a"+"b"优化为"ab",因为"a"和"b"都已经是常量了。而上一行的是变量,所以不会优化。
        
        System.out.println( s3 == s4 );		//false
    }
}

5.5 StringTable特性

  • 常量池中的字符串仅是符号,第一次用到时才变为对象。
  • 利用串池的机制,来避免重复创建字符串对象。
  • 字符串变量拼接的原理是 StringBuilder
  • 字符串常量拼接的原理是编译期优化。
  • 可以使用 intern() 方法,主动将串池中还没有的字符串对象放入串池。
    • 1.8 将这个字符串对象尝试放入串池,如果有则不会放入,如果没有则会放入串池,会把串池中的对象返回。
    • 1.6 将这个字符串对象尝试放入串池,如果有则不会放入,如果没有会把此对象复制一份,放入串池,会把串池中的对象返回。

例:

public static main(String[] args){
    String s = new String("a") + new String("b");	// new String("ab")
    //串池	StringTable: [ "a","b" ]
    //堆	new String("a")	, new String("b")	, new String("ab")
    
    //再执行如下代码
    String s2 = s.intern();	//将这个字符串对象尝试放入串池,如果有则不会放入,如果没有则放入串池,会把串池中的对象返回。
    //串池	StringTable: [ "a","b","ab" ]
    //堆	new String("a")	, new String("b")	, new String("ab")
    
    System.out.println(s2=="ab");	//true
    System.out.println(s=="ab");	//true
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/365018.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Hadoop集群模式安装(Cluster mode)

1、Hadoop源码编译 安装包、源码包下载地址 Index of /dist/hadoop/common/hadoop-3.3.0为什么要重新编译Hadoop源码? 匹配不同操作系统本地库环境,Hadoop某些操作比如压缩、IO需要调用系统本地库(*.so|*.dll) 修改源码、重构源码 如何…

电子墨水屏的应用场景

电子纸挺好个东西,大家都把注意力集中在商超场景,其实还有更多有趣的场景方案可用,价值也不小,比如: 仓库场景:通过亮灯拣选,提高仓库作业效率。 仓库循环使用标签:做NFC类发卡式应…

CnOpenData专精特新“小巨人”企业工商注册基本信息数据

一、数据简介 “专精特新”一词最早来源于2011年7月,由时任工信部总工程师朱宏任在《中国产业发展和产业政策报告(2011)》新闻发布会上首次提出。“专精特新”是指具备专业化、精细化、特色化、创新型四大优势的企业。根据工信部的定义&#…

content-type几种常见类型区别

Content-Type叫做MIME(mediaType)类型,使用Content-Type来表示请求和响应中的媒体类型信息。如果是请求头,它用来告诉服务端如何处理请求的数据,如果是响应头,它用来告诉客户端(一般是浏览器)如…

numpy的常见数据类型

常见数据类型介绍Python 原生的数据类型相对较少, bool、int、float、str等。这在不需要关心数据在计算机中表示的所有方式的应用中是方便的。然而,对于科学计算,通常需要更多的控制。为了加以区分 numpy 在这些类型名称末尾都加了“_”。类型…

【ESP32+freeRTOS学习笔记-(九)事件组】

目录1、概述2、事件组的特性2.1 事件组、事件标志和事件位2.2 事件组位长的设置2.3 多任务访问3、使用事件组管理事件3.1 xEventGroupCreate()3.2 xEventGroupSetBits()3.3 xEventGroupGetBits()3.4 xEventGroupWaitBits()3.5 示例4、使用事件组同步任务4.1 xEventGroupSync()4…

MobaXterm安装与使用

MobaXterm安装与使用 我们首先进入MobaXterm官网,其提供了收费版和免费版,我们使用免费版即可 随后便是安装过程了,很简单。解压后运行该文件一路next即可。 安装完成后我们便可以使用了点击session 选择SSH连接方式,输入服务器…

vue项目——获取指定日期是周几和第几周的信息——表格展示

最近在写后台管理系统,遇到以下的要求,就是要展示 年月日和周几和第几周的情况。 下面记录一下用到的函数: 1.跟据日期获取第几周 //根据日期获取第几周 getWeek(dateTime) {let temptTime new Date(dateTime);//周几let weekday temptT…

浏览器用一行JS代码导出cookies.txt,Python的requests库导入cookies格式化为字典格式

在Python进行爬虫时,如果仅使用requests库打开某个网页,requests的session.cookies保存的cookies信息少得可怜,有时cookies甚至是空白!但浏览器里打开同一个网页,cookies信息非常详尽,比如浏览器的cookies保…

2023-02-22 学习记录--TS-邂逅TS(二)

TS-邂逅TS(二) 不积跬步,无以至千里;不积小流,无以成江海。💪🏻 一、接口(interface) 在 ts 中,子类只能继承一个父类,不可多继承,但是…

2020蓝桥杯真题单词分析 C语言/C++

题目描述 小蓝正在学习一门神奇的语言,这门语言中的单词都是由小写英文字母组 成,有些单词很长,远远超过正常英文单词的长度。小蓝学了很长时间也记不住一些单词,他准备不再完全记忆这些单词,而是根据单词中哪个字母出…

恭喜!龙蜥社区荣登 2022 科创中国“开源创新榜”

2 月 20 日,中国科协召开以“创新提振发展信心,科技激发产业活力”为主题的2023“科创中国”年度会议。会上,“科创中国”联合体理事长、中国工程院院士周济介绍了 2022 年系列榜单征集遴选情况,并与中国科协副主席、中国工程院院…

音箱上8键触摸芯片绿芯GTC08L完美替换启攀微

由工采网代理提供的韩国GreenChip电容式触摸芯片-GTC08L是GreenTouch5CTM电容式触摸传感器系列之一;可以在发动机运行下进行8通道电容传感;对电磁兼容、电磁干扰、温湿度变化、电压干扰、温度漂移、湿度漂移等都有较强的抗干扰能力。不会对CS, RS,EFT&am…

在vue3+ts的项目中,如何解决vant组件自带表单校验不生效?

问题描述: 点击发送验证码后,为了让逻辑更加严谨,使用了vant组件自带的表单校验,进行二次校验,防止验证码发送成功后,登录手机号被二次修改,但根据官网描述cv之后不生效,甚至连获取…

3年自动化测试,月薪1.2W,不敢跳槽,每天都很焦虑

在我们的身边,存在一个普遍现象:很多人从事软件测试坎,不计其数,经历的心酸难与外人道也。可是技术确难以提升、止步不前,薪资也只能看着别人水涨船高,自己却没有什么起色。虽然在公司里属于不可缺少的一员…

轮播图、阅读注册协议、网页时钟、随机点名、小米搜索框、轮播图点击切换——web APIs练习

目录 一、获取元素(DOM) 1. 随机轮播图案例 2. 阅读注册协议(定时器间歇函数的应用) 3. 轮播图定时器版 4. 网页时钟 二、事件基础(DOM) 1. 随机点名案例 2. 轮播图点击切换(重点&#…

Python、Java、JavaScript、C、Go等编程语言如何实现“定时器”功能

这是CSDN平台2月推出的一个活动(活动链接为:CSDN 征文活动),聊聊时间的话题,小编我也不知道有什么好聊的时间的话题,看了CSDN给出的部分话题上,有一个这样的话题,如何用各种编程语言实现“定时器”&#xf…

初识SpringSpring核心容器

初识Spring Spring生态: Spring FrameWork发展: Spring FrameWork系统架构: Spring FrameWork学习路线: Spring核心概念 Ioc DI 实现IoC 1、导入Spring坐标 2、定义Spring管理的类(接口) 3、创建spring配…

tensorflow 学习笔记(三):神经网络八股

本节内容: 前两节使用 Tensorflow2 的原生代码大叫神经网络。本节使用 keras 搭建神经网络(八股:六步法,有 Sequential 和 class 两种)。 文章目录一、搭建网络八股 sequential1.1、keras 介绍1.2、六步法搭建 keras …

Mac搭建appium+python+Android自动化环境

mac搭建appium+python+Android自动化环境 一、安装jdk二、安装Android-SDK三、配置 Android 环境四、安装Appium五、打开appium一、安装jdk 自己百度 二、安装Android-SDK 1)下载地址: http://www.android-studio.org/index.php/download 下载mac版本的dmg 2) 下载完成后…