十二.吊打面试官系列-JVM优化-深入JVM内存模型

server/2024/9/20 6:05:17/ 标签: jvm

JVM内存模型

1.JVM的组成

整个JVM组成由 :运行时数据区 , 类加载子系统 , 执行引擎 , 本地方法库 几部分组成
在这里插入图片描述
上面是Java7的内存模型,Java8以后做了一些调整,把方法区变成了元空间,元空间不在JVM中,而使用直接内存(计算机内存)

  1. 运行时数据区

    见名知意,运行时数据区是Java虚拟机在执行Java程序时,用于存储和管理运行时数据的内存区域,运行时数据区由: 方法区,虚拟机栈,本地方法栈,程序计数器,堆 五部分组成,其中堆和方法区是线程共享的,其他区域是线程隔离的,也就是线程私有,比如:程序计数器在每个线程执行的时候都有一个私有的程序计数器

  2. 类加载子系统

    类加载器子系统的主要作用是负责从文件系统或网络中加载.class文件,并将其加载到JVM中。加载后的类信息存放在方法区(或JDK 8及以后版本的元空间)中,具体的执行则交给执行引擎去操作

  3. 执行引擎

    JVM(Java Virtual Machine)执行引擎是JVM的核心组件之一,它负责将编译后的字节码(Bytecode)解释成可执行的机器指令。而JIT(Just-In-Time)编译器则是执行引擎中的一个重要部分,用于提高Java程序的执行效率。

    JVM执行引擎的主要任务是将字节码转换为特定平台上的本地机器指令,以便在JVM上执行。它主要包括解释器(Interpreter)和JIT编译器两部分。

    解释器是JVM执行引擎的基本组件之一,它采用逐行解释的方式执行字节码。当JVM启动时,解释器会根据预定义的规范对字节码进行解释,将每条字节码指令“翻译”为对应平台的本地机器指令并执行。然而,由于解释执行的方式效率较低,因此JVM还引入了JIT编译器来优化性能。

  4. JIT编译器

    JIT编译器是JVM执行引擎中的另一个重要组件,它可以在程序运行时将频繁执行的字节码编译为本地机器码,从而提高执行效率。JIT编译器通过动态编译技术,将热点代码(即频繁执行的代码)优化为本地机器码,并存储在代码缓存区中。在后续的执行中,JVM可以直接从代码缓存区中加载机器码并执行,而无需再次解释字节码。这种方式可以显著提高Java程序的执行速度

  5. 本地方法库接口

    本地方法库接口(Native Method Interface,简称JNI),它允许Java代码与其他语言写的代码进行交互。JNI是Java虚拟机(JVM)提供的一组函数和协议,用于Java应用程序与本地应用程序(如C、C++编写的程序)进行交互。本地方法库就是C,C++编写的一些代码库,比如: xxx.dll文件,在Java中很多功能底层就是C++来实现的。

下面我们针对这些区域详细来说明

2.程序计数器

程序计数器是线程私有的,虽然名字叫计数器,但主要用途还是用来确定指令的执行顺序,比如循环,分支,跳转,异常捕获等。它记录了当前线程执行的字节码指令的地址。JVM对于多线程的实现是通过轮流切换线程实现的,所以为了保证每个线程都能按正确顺序执行,将程序计数器作为线程私有.程序计数器是唯一一个JVM没有规定任何OOM的区块.(out of memory)

程序计数器是一块非常小的内存空间,可以看做是当前线程执行字节码的行号指示器,每个线程都有一个独立的程序计数器,因此程序计数器是线程私有的一块空间,此外,程序计数器是Java虚拟机规定的唯一不会发生内存溢出的区域

3.方法区(元空间)

方法区主要用于存储虚拟机加载的类信息、常量、静态变量,以及编译器编译后的代码等数据。在jdk1.7及其之前,方法区是堆的一个“逻辑部分”(一片连续的堆空间),但为了与堆做区分,方法区还有个名字叫“非堆”,也有人用“永久代”(HotSpot对方法区的实现方法)来表示方法区。

jdk1.7已经开始准备“去永久代”的规划,jdk1.7的HotSpot中,已经把原本放在方法区中的静态变量、字符串常量池等移到堆内存中,(常量池除字符串常量池还有class常量池等),这里只是把字符串常量池移到堆内存中;在jdk1.8中,方法区已经不存在,原方法区中存储的类信息、编译后的代码数据等已经移动到了元空间(MetaSpace)中,元空间并没有处于堆内存上,而是直接占用的本地内存(NativeMemory)。元空间的大小仅受本地内存限制
在这里插入图片描述
JVM在后续版本中去掉永久代(PermGen)的原因主要有以下几点:

  • 避免内存溢出(OOM)问题:永久代存储的是类信息、常量、静态常量等信息,因此存储空间不容易界定,容易发生内存溢出问题(如常见的java.lang.OutOfMemoryError: PermGen space异常)。在Java 8中,采用了元空间(Metaspace)代替永久代,元空间使用的是直接系统内存,可以动态地扩展和收缩,从而避免了永久代空间不足导致的内存溢出问题。
  • 提高垃圾回收效率:相对于新生代回收效率(通常为70~95%),永久代的垃圾收集效率较低。这主要是因为永久区的回收机制相对新生代来说更为复杂,要求条件更多。去掉永久代后,采用元空间可以更好地管理类的元数据,提高垃圾回收的效率。
  • 统一内存管理:去掉永久代也是为了更好地融合HotSpot JVM与其他JVM(如JRockit VM),因为JRockit VM并没有永久代的概念。这样做可以使得JVM的内存管理更加统一和标准化。
  • 避免内存泄漏:永久代中的某些数据(如类加载器)可能在不再需要时仍然被引用,导致内存泄漏。采用元空间后,可以更好地管理这些数据,避免内存泄漏问题。

需要注意的是,虽然永久代已经被去掉,但方法区(Method Area)的概念仍然存在。方法区主要用于存储类的信息、常量池、方法数据、方法代码等。在Java 8及以后的版本中,方法区的实现由元空间来完成。Java8内存模型如下
在这里插入图片描述

4.虚拟机栈

Java虚拟机栈也是线程私有的,每个方法执行都会创建一个栈帧,局部变量就存放在栈帧中,还有一些其他的动态链接之类的.

虚拟机会为每个线程分配一个虚拟机栈,每个虚拟机栈中都有若干个栈帧,每个栈帧中存储了局部变量表、操作数栈、动态链接、返回地址等。一个栈帧就对应Java代码中的一个方法,当线程执行到一个方法时,就代表这个方法对应的栈帧已经进入虚拟机栈并且处于栈顶的位置,每一个Java方法从被调用到执行结束,就对应了一个栈帧从入栈到出栈的过程
在这里插入图片描述

  • 栈帧(方法执行形成栈帧):栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构,线程私有。栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息。每一个方法从调用至执行完成的过程,都对应着一个栈帧在虚拟机栈里从入栈到出栈的过程,栈帧随着方法调用而创建,随着方法结束而销毁

  • 局部变量表(储存方法参数和局部变量):局部变量表(Local Variable Table)是一组变量值存储空间,用于存放方法参数和方法内定义的局部变量。局部变量表的容量以变量槽(Variable Slot)为最小单位,Java虚拟机规范并没有定义一个槽所应该占用内存空间的大小,但是规定了一个槽应该可以存放一个32位以内的数据类型。

  • 操作数栈(用于计算的临时数据存储区):操作数栈(Operand Stack)也常称为操作栈,它是一个后入先出栈(LIFO),当一个方法刚刚开始执行时,其操作数栈是空的,随着方法执行和字节码指令的执行,会从局部变量表或对象实例的字段中复制常量或变量写入到操作数栈,再随着计算的进行将栈中元素出栈到局部变量表或者返回给方法调用者,也就是出栈/入栈操作。一个完整的方法执行期间往往包含多个这样出栈/入栈的过程。

  • 动态链接(用来转化方法的内存地址直接引用的):在一个class文件中,一个方法要调用其他方法, 需要将这些方法的符号引用转化为其在内存地址中的直接引用,而符号引用存在于方法区中的运行时常量池。

  • 返回地址:方法的返回出口,一般就是调用该方法的上一个方法的地址。

下面是一个类中的方法执行的详细流程:以Hello.class 为例

  1. 首先当使用到Hello的时候比如:new Hello(),类加载子系统会把Hello.class 加载到JVM中,存储到元空间,并且会把 new Hello 对象实例存储到堆中
  2. 程序是通过执行引擎去执行的,它通过驱动线程来执行Hello,线程会分得一个私有的虚拟机栈,每个方法执行都会形成栈帧进行压栈。
  3. 随着方法的调用结束,后被调用的方法就先出栈,最先被调用的方法最后出栈,随着方法全部执行完成,栈帧释放,线程结束。
  4. 如果方法中有对象的引用,那么方法结束后局部变量也被释放,那堆中的对象实例就没有引用关系将被识别为垃圾,等待垃圾回收器回收
    在这里插入图片描述

5.本地方法栈

本地方法栈与虚拟机栈的区别是,虚拟机栈执行的是Java方法,本地方法栈执行的是本地方法(Native Method),其他基本上一致,在HotSpot中直接把本地方法栈和虚拟机栈合二为一,这里暂时不做过多叙述。

6.堆内存

堆和方法区一样(确切来说JVM规范中方法区就是堆的一个逻辑分区),就是一个所有线程共享的,存放对象的区域,也是GC的主要区域.其中的分区分为新生代(YoungGeneration),老年代(OldGeneration).新生代中又可以细分为一个Eden,两个Survivor区(From,To).Eden中存放的是通过new 或者newInstance方法创建出来的对象

绝大多数都是很短命的.正常情况下经历一次gc之后,存活的对象会转入到其中一个Survivor区,然后再经历默认15次的gc,就转入到老年代.这是常规状态下,在Survivor区已经满了的情况下,JVM会依据担保机制将一些对象直接放入老年代。`
在这里插入图片描述
文章到此结束,码字很辛苦,如果对你有所帮助请给个好评。下一章:深入分析class字节码文件结构


http://www.ppmy.cn/server/41897.html

相关文章

领导者十二问答【关心部属、钦佩之意、不败、抱着当老二心情来当老大、用他人的长处、先赞成,后反对】

目录 领导出差不在如何管理? 当合作者做决策时候,没有跟你商量,如何处理? 处理人际关系,比较极端,如何调整? 未来管理的发展方向? 关于信心? 如何制定目标&#xff…

数据结构(二)关于空间的使用

数据结构(二)关于空间的使用 要点:解决问题的效率跟空间的利用效率有关 思考:如何空间的利用效率 01 如何空间的利用效率 参考:递归导致的栈溢出 Exception in thread "main" java.lang.StackOverflowE…

16:00面试,16:06就出来了,问的问题过于变态了。。。

从小厂出来,没想到在另一家公司又寄了。 到这家公司开始上班,加班是每天必不可少的,看在钱给的比较多的份上,就不太计较了。没想到5月一纸通知,所有人不准加班,加班费不仅没有了,薪资还要降40%…

Linux备份---异地

参考文档:Linux环境实现mysql所在服务器定时同步数据文件到备份服务器(异地容灾备份场景)_mysql异地备份-CSDN博客 通过SSH进行连接: 应用服务器: 通过ssh-keygen -t rsay建立ssh通信的密钥 密钥建立后,…

阿里云 服务之前设置的密钥登陆,关闭了密码登录,现在打开密码登录

通过网页远程链接 切换用户 sudo -i 输入vim /etc/ssh/sshd_config 进入配置文件 找到 将这一项设置为yes 重启系统 systemctl restart sshd.service

某能源集团电力公司搭建数据报表中心,实现采集填报分析一体化

​在当今这个信息爆炸的时代,数据已成为企业最宝贵的财富,越来越多的企业开始重视数据的积累和归集。在企业日常生产和工作过程中,会产生绵延不断的数据,但这些数据往往没有统一的记录、归纳和整理,或者录入了系统却分…

将excel表中的数据导入到navicat中

1.将excel中的表头改成英文 2.在navicat中右键表,选择【导入向导】 3.在弹出的导入向导中选择Excel文件,然后点击【下一步】 4.选择需要导入的excel,选中后,在下方会罗列出excel中的sheet,勾选需要导入的sheet&#xf…

通义千问 1.5 -7B fine-tune验证

尝试对对中文数据进行finetune验证,测试模型的可优化方向。下面是代码的详细情况 代码实现 from datasets import load_dataset from transformers import (AutoModelForCausalLM,AutoTokenizer,BitsAndBytesConfig,HfArgumentParser,AutoTokenizer,TrainingArgum…

以下是服务器的一些主要作用:

2. 数据存储:服务器可以用来存储大量数据,如文件、图片、视频和音频等。此外,服务器还可以存储应用程序和数据库,以支持各种业务需求。

单例模式如何实现?

单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问这个唯一实例。在 C 中,可以通过将构造函数设为私有,并提供一个静态方法来获取或创建类的实例来实现单例模式。 下面是一个简单的 C 单例模式的…

东莞酷得电子方案 遥控水弹坦克车

首先遥控小车是一种能够通过无线遥控器进行远程操控的小型机器人。遥控小车应用了哪些软硬件技术呢?本文将从以下几个方面进行详细介绍。 遥控小车应用了多种软硬件技术,涉及底盘结构、动力系统、传感器、控制器等多个方面。 底盘结构:遥控…

深入理解K8S【安全认证机制kubectlconfig】

深入理解K8S【安全认证机制】 1 核心概念 1.1 安全体系 对于大型系统来说,对业务的权限、网络的安全认证是必不可少的。 对于linux系统来说,用户和组、文件权限、SELinux、防火墙、pam、sudo等,究其核心的目的都是为了保证系统是安全的。 …

如何使用Python进行网页爬取

Python爬虫案例可以有很多种,但我会为你提供一个简单的案例,该案例使用Python的requests库来爬取一个网页的内容,并使用BeautifulSoup库来解析HTML并提取特定的信息。 假设我们要从某个新闻网站(例如:示例网站&#x…

做抖店的门槛高吗?一个月的时间能入门吗?基础问题解答如下

我是王路飞。 抖店,依旧是普通人做抖音最好的渠道,没有之一,依旧值得我们all in。 这是我对2024年抖音小店的看法和态度, 那么做抖店的门槛高吗?新手用一个月的时间能做到入门吗?投入和回报的数据是多少…

Java提供了哪些IO方式❓NIO如何实现多路复用❓

I/O 方式🍑 1)同步阻塞 I/O( BIO )✨ 定义与特点:在BIO模型中,当一个线程执行I/O操作如读写数据时,该线程会被阻塞,直到I/O操作完成。这意味着在I/O操作进行期间,该线程…

GoF之代理模式(静态代理+动态代理(JDK动态代理+CGLIB动态代理带有一步一步详细步骤))

1. GoF之代理模式(静态代理动态代理(JDK动态代理CGLIB动态代理带有一步一步详细步骤)) 文章目录 1. GoF之代理模式(静态代理动态代理(JDK动态代理CGLIB动态代理带有一步一步详细步骤))每博一文案2. 代理模式的理解3. 静态代理4. 动…

现实投资者怎么摆脱伦敦银波动影响?

有伦敦银投资经验的朋友会发现,即便自己找到了伦敦银市场的趋势,但是总是没办法坚守自己的仓位,因为没办法摆脱伦敦银波动的影响,比方说在上升趋势中买入后,投资者总是是觉得伦敦银价格会反转下跌,所以他就…

Vagrant + docker搭建Jenkins 部署环境

有人问,为什么要用Jenkins?我说下我以前开发的痛点,在一些中小型企业,每次开发一个项目完成后,需要打包部署,可能没有专门的运维人员,只能开发人员去把项目打成一个war包,可能这个项…

微信小程序中的图像奥秘:图片与Base64的华丽变身记

微信小程序中的图像奥秘:图片与Base64的华丽变身记 基本概念解析图片与Base64的关系为何转换 图片转Base64实战微信小程序使用wx.getImageInfo获取图片信息图片转换为Base64注意 Base64转图片直接在小程序页面显示云开发环境转换注意 遇遇问题排查思路结语引发讨论 …

线程与进程___(一)

1、线程 Thread 类创建得线程为前台线程,线程池中的为后台线程,,,Main方法结束后,前台线程仍然运行,直到完成,而后台线程立刻结束。 调用线程时候不会立刻进入 Running 状态, 而是…