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

news2024/9/23 5:22:25

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.coloradmin.cn/o/1679604.html

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

相关文章

Linux 第三十三章

🐶博主主页:ᰔᩚ. 一怀明月ꦿ ❤️‍🔥专栏系列:线性代数,C初学者入门训练,题解C,C的使用文章,「初学」C,linux 🔥座右铭:“不要等到什么都没有了…

Golang | Leetcode Golang题解之第80题删除有序数组中的重复项II

题目&#xff1a; 题解&#xff1a; func removeDuplicates(nums []int) int {n : len(nums)if n < 2 {return n}slow, fast : 2, 2for fast < n {if nums[slow-2] ! nums[fast] {nums[slow] nums[fast]slow}fast}return slow }

1、sql server数据库进行sql注入

靶机取自&#xff1a;墨者sql server 1、判断数据库类型 抓包知sql server&#xff0c;所以注入语句跟MySQL有些区别 2、判断注入点 “http://219.153.49.228:42514/new_list.asp?id2 ”&#xff0c;当id2 and 11时显示正确&#xff0c;id2 and 12时页面报错。 3、确定列…

Vue.js的发展史(一)

Vue.js的发展史&#xff08;一&#xff09; 什么是Vue? Vue (发音为 /vjuː/&#xff0c;类似 view) 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建&#xff0c;并提供了一套声明式的、组件化的编程模型&#xff0c;帮助你高效地开发…

云服务器和物理机该怎样分别呢

随着网络的不断发展&#xff0c;服务器的类型也在以不同的方式更新。现在云服务器的兴起占据了很大一部分市场&#xff0c;物理机的市场份额受到了很大的冲击。物理机和云服务器有什么区别&#xff1f;如何选择适合自己需求的&#xff1f;虽然物理服务器和云服务器都是服务器&a…

Python进度条工具——tqdm

原文链接&#xff1a;http://www.juzicode.com/python-note-tqdm 在安装Python库文件的时候我们经常可以看到这种进度条&#xff1a; 其实Python库中就自带了现成的工具库——tqdm。 tqdm读起来比较拗口&#xff0c;它是从“进程”的阿拉伯语taqaddum简化而来。 安装tqdm 使用…

有关登录安全,测试人该知道些什么?

作为测试&#xff0c;给我们一个关键词“登录”&#xff0c;我们可能想到的用例设计更多的是什么用户名、密码校验是否合法、是否为空、是否正确等等之类的场景。 但在如今信息化的时代&#xff0c;“登录安全”已经是一个很热门且普遍的的话题了&#xff0c;今天给大家简单分…

uniapp小程序控制页面元素滚动指定距离

要实现页面元素滚动&#xff0c;最好还是使用 scroll-view 来实现&#xff0c;官方文档地址&#xff1a;scroll-view | uni-app官网 通过设置scroll事件来实现滚动监听&#xff0c;当滚动的元素的时候&#xff0c;就会触发这个事件&#xff0c;并且事件里面包含有滚动距离&…

哈希表的理解和实现

目录 1. 哈希的概念 (是什么) 2. 实现哈希的两种方式 (哈希函数) 2.1. 直接定址法 2.2. 除留余数法 2.2.1. 哈希冲突 3. 补充知识 3.1. 负载因子 3.2. 线性探测和二次探测 4. 闭散列实现哈希表 (开放定址法) 4.1. 开放定址法的实现框架 4.2. Xq::hash_table::insert…

实时美颜技术揭秘:直播美颜SDK的架构与优化

当下&#xff0c;美颜技术成为直播平台吸引用户和提升用户体验的重要手段。本文将揭秘实时美颜技术&#xff0c;详细介绍直播美颜SDK的架构&#xff0c;并探讨其优化方法。 一、实时美颜技术概述 1、发展历程 随着图像处理算法的进步&#xff0c;逐渐发展到实时视频处理领域…

醉了,面个功能测试,还问我Python装饰器

Python 装饰器是个强大的工具&#xff0c;可帮你生成整洁、可重用和可维护的代码。某种意义上说&#xff0c;会不会用装饰器是区分新手和老鸟的重要标志。如果你不熟悉装饰器&#xff0c;你可以将它们视为将函数作为输入并在不改变其主要用途的情况下扩展其功能的函数。装饰器可…

走进开源,拥抱开源

走进开源&#xff0c;拥抱开源 一、开源文化1.1 什么是开源1.2 为什么要开源1.3 有哪些开源协议 二、选择开源2.1 开源社区的类型与特点2.2 如何选择开源社区2.3 如何选择开源项目 三、参与开源3.1 开源社区的参与方式3.2 开源项目的参与方式 四、Apache Doris 参与示例4.1 Dor…

随笔:棋友们

我是在小学二年级学会中国象棋的&#xff0c;准确说&#xff0c;是学会象棋的下棋规则的&#xff0c;师傅是二舅。我最早的对手就是同学波仔。波仔比我略早学会象棋&#xff0c;总用连珠炮欺负我&#xff0c;开局几步棋就把我将死。我不知道怎么破解。轮到我先走时&#xff0c;…

降Compose十八掌之『亢龙有悔』

公众号「稀有猿诉」 原文链接 降Compose十八掌之『亢龙有悔』 Jetpack Compose是新一代的声明式的UI开发框架&#xff0c;由Google在2019年推出&#xff0c;最初是作为Android的新式UI开发框架&#xff0c;但它本质是一个声明式UI开发框架&#xff0c;并不受制于底层的平…

机器人非线性系统反馈线性化与解耦

机器人非线性系统的反馈线性化和解耦是控制理论中的两个重要概念&#xff0c;它们分别用于简化系统分析和设计过程&#xff0c;提高控制系统的性能。 首先&#xff0c;反馈线性化是一种将非线性系统转化为线性系统的技术。在机器人控制中&#xff0c;由于机器人本身是一个强耦…

每日一日 kotori和气球

kotori和气球 (nowcoder.com) 题目描述&#xff0c;就是只要相邻的气球不相同即可&#xff0c; 解题思路 使用高中的排列组合&#xff1a;第一个位置 可以填n种情况 其次后推不可与前一个相同所以可以 填n -1中情况&#xff0c;结果相乘即可 可以使用bigInteger实现 或者说…

[Kubernetes] kube-proxy 详解

文章目录 1.kube-proxy概述2.userspace模式3.iptables模式4.ipvs模式 1.kube-proxy概述 kube-proxy组件是用来实现service的请求转发&#xff0c;具体实现方式是kube-proxy运行在每个node上&#xff0c;通过watch监听API Server 中service资源的create&#xff0c;update&…

Spring 各版本发布时间与区别

版本版本特性Spring Framework 1.01. 所有代码都在一个项目中 2. 支持核心功能IoC、AOP 3. 内置支持Hibernate、iBatis等第三方框架 4. 对第三方技术简单封装。如&#xff1a;JDBC、Mail、事务等 5. 只支持XML配置方式。6.主要通过 XML 配置文件来管理对象和依赖关系&#xff0…

【2024华为HCIP831 | 高级网络工程师之路】刷题日记(18)

个人名片&#xff1a;&#x1faaa; &#x1f43c;作者简介&#xff1a;一名大三在校生&#xff0c;喜欢AI编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;落798. &#x1f43c;个人WeChat&#xff1a;hmmwx53 &#x1f54a;️系列专栏&#xff1a;&a…