当前位置: 首页 > news >正文

JVM内存结构介绍

8a4f26c1086942dda8108e9d9dcfc06c.jpg1. 什么是JVM

 

我们都知道在 Windows 系统上一个软件包装包是 exe 后缀的,而这个软件包在苹果的 Mac OSX 系统上是无法安装的。类似地,Mac OSX 系统上软件安装包则是 dmg 后缀,同样无法在 Windows 系统上安装。

 

Java 代码为什么可以在 Windows 系统运行,也可以在 Linux 系统运行?这就是 jvm 的功劳, Java 虚拟机可以理解为一个翻译官,在 Linux 系统上将 Java 代码翻译成 Linux 机器码,在 Windows 系统上将 Java 代码翻译成 Windows 机器码,所以 Java 有了虚拟机之后,可以让 Java 代码运行在不同的系统上。

 

2. JVM内存结构(运行时数据区)

运行时数据区是官方说法,但很多时候这个名词并不是很形象,再加上日积月累的习惯,很多人都习惯用 JVM 内存结构这个说法

 

JVM 内存结构(运行时数据区)主要包括:堆、栈(虚拟机栈)、本地方法栈、方法区、程序计数器等。

 

线程公有:堆、方法区

线程私有:栈、本地方法栈、程序计数器

 

 

2.1 堆(公有)

堆内存被所有线程共享。堆内存用于存放由 new 创建的对象和数组。

 

Java堆分为年轻代(Young Generation)和老年代(Old Generation);年轻代又分为乐园(Eden)和幸存区(Survivor区);幸存区又分为 From 区(From Survivor 区)和 To 区(To Survivor 区)。

 

而 JVM 垃圾回收机制主要收集堆中年轻代和老年代对象所占用的内存空间。

 

 

 

为什么默认的虚拟机配置,Eden:from :to = 8:1:1 ?

 

这是经过大量统计得出的结果发现 80% 的对象存活时间都很短,于是将 Eden 区设置为年轻代的 80%,这样可以减少内存空间的浪费,提高内存空间利用率。

 

年轻代中的 Minor GC

 

1、绝大多数刚刚被创建的对象会存放在乐园(Eden)。

2、在乐园内存满时,执行第一次GC(Minor GC)之后,存活的对象被移动到其中一个幸存区(Survivor)。

3、此后,每次乐园执行GC后,存活的对象会被堆积在同一个幸存区。

4、当一个幸存区饱和,还在存活的对象会被移动到另一个幸存区。然后会清空已经饱和的那个幸存区。

5、在以上步骤中重复N次(N = MaxTenuringThreshold(年龄阀值设定,默认15))依然存活的对象,就会被移动到老年代。

 

从上面的步骤可以发现,两个幸存者空间,必须有一个是保持空的。

 

需要重点记住的是,对象在刚刚被创建之后,是保存在乐园的(Eden)。那些长期存活的对象会经由幸存区(Survivor)转存到老年代(Old generation)。

 

也有例外出现,对于一些比较大的对象(需要分配一块比较大的连续内存空间)则直接进入到老年代(内存分配担保机制)。

 

注意:当年轻代满时就会触发Minor GC,这里的年轻代满指的是Eden满,Survivor满不会引发Minor GC。由于年轻代中的对象存活时间比较短,Minor GC比较频繁,GC 速度也很快。

 

老年代中的 Full GC

 

老年代空间的构成很简单,它不像新生代空间那样划分为几个区域,它只有一个区域,里面存储的对象并不像新生代一样存活时间很短。这里的对象几乎都是从Survivor 区中熬过来的,它们绝不会轻易的被回收掉。

 

注意:Full GC 是清理整个堆空间,包括年轻代和老年代,如果Full GC之后,堆中仍然无法存储对象,就会抛出OutOfMemoryError异常。由于老年代内存不会轻易的被回收掉,因此 Full GC 发生的次数不会有 Minor GC 那么频繁。但是老年代内存大,做一次 Full GC 的时间比 Minor GC 要更长(约10倍)。

 

2.2 方法区(公有)

方法区被所有线程共享。方法区用于存放静态变量、常量、类信息(版本、方法、字段等)、常量池。可以看做是将类(Class)的元数据,保存在方法区里。

 

Integer常量池

 

都知道数据类型 == 比较的是内存地址,先看个下边的例子。

 

public static void main(String[] args)

{

    Integer i1 = 66;

    Integer i2 = 66;

    Integer i3 = 150;

    Integer i4 = 150;

    System.out.println(i1 == i2);//true

    System.out.println(i3 == i4);//false

}

i1 == i2 结果为 true,i3 == i4 结果为 false。由结果得知 i1 和 i2 的内存地址是相同的,而 i3 和 i4 内存地址是不同的。

 

产生这样结果的原因是 Integer i1 = 66 实际上有一步装箱的操作,通过 Integer 的 valueOf 方法将 int 型的 66 装箱成 Integer。下边是 Integer 中的 valudOf 方法。

 

public static Integer valueOf(int i) {

    if (i >= IntegerCache.low && i <= IntegerCache.high)

        return IntegerCache.cache[i + (-IntegerCache.low)];

    return new Integer(i);

}

Integer 的 valueOf 方法很简单,它判断变量是否在 IntegerCache 的最小值(-128)和最大值(127)之间,如果在,则返回常量池中的内容,否则 new 一个 Integer 对象。

 

由于 66 在 -128 ~ 127 之间,所以 66 装箱时,使用的是常量池中的 66,所以 == 结果为 true。

而 150 不在范围内,在装箱时执行了 new Integer(150),所以返回的是新创建的对象,所以 == 结果为 false。

 

String常量池

 

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

 

// 1

String str = new String("abc");

// 2

String str = abc;

第一种:使用 new 创建的对象,存放在堆中,每次 new 出来的内存地址都不同。

第二种:先在常量池中找有没有 “abc”。有,则直接取常量池内存地址赋值给 str。没有,先在常量池创建“abc”,再取内存地址赋值给 str。

 

通过代码验证上面理论。

 

public static void main(String[] args) {

    String s1 = new String("abc");

    String s2 = new String("abc");

    String s3 = "abc";

    String s4 = "abc";

    System.out.println(s1 == s2);// false

    System.out.println(s3 == s4);// true

}

s1 == s2 为 false 原因:str1 和 str2 使用 new 创建对象,分别在堆上创建了不同的对象。两个引用指向堆中两个不同的对象,所以为 false。

s3 == s4 为 true 原因:首先在栈上存放变量引用 s3,然后去常量池中找是否有 abc,没有,则将 abc 存储在常量池中,然后将 s3 指向常量池的 abc。当 s4 = "abc" 时,去常量池中发现已经有 abc 了,就将 s4 引用指向常量池已有的 abc 。所以s3 == s4,指向同一个内存地址。

String 类中有一个方法 intern,可以返回池中的字符串,如下代码

 

public static void main(String[] args) {

    String s1 = new String("abc");

    String s2 = "abc";

    System.out.println(s1 == s2);// false

    System.out.println(s1.intern() == s2);// true

}

上边的结果可以看下 intern() 方法注释就知道结果。当调用 intern 方法时,如果常量池中已经该字符串,则返回池中的字符串;否则将此字符串添加到常量池中,并返回字符串的引用。

 

2.3 栈(私有)

栈是后进先出的。栈是线程私有的,他的生命周期与线程相同。每个线程都会分配一个栈的空间,一个线程会对应一个栈。

 

栈存储什么

 

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

 

使用递归时,会导致 StackOverflowError 错误,就是因为不断的在栈中创建栈帧,当栈帧的数量超过了栈的大小时,就会导致报错。

 

 

2.4 本地方法栈(私有)

本地方法栈是线程私有的,主要为 JVM 使用到的 Native 方法服务。Native 方法不是以 Java 语言实现的,而是以本地语言实现的(比如 C 或 C++)。

 

可以理解为 Native 方法是与操作系统直接交互的,比如通知垃圾收集器进行垃圾回收的代码 System.gc(),获取常量池中的字符串引用 String.intern(),都是使用 native 修饰的。

 

2.5 程序计数器(私有)

程序计数器是一个比较小的内存区域,可能是CPU寄存器或者操作系统内存,其主要用于指示当前线程所执行的字节码执行到了第几行,可以理解为是当前线程的行号指示器。

 

字节码解释器在工作时,会通过改变这个计数器的值来取下一条语句指令。 每个程序计数器只用来记录一个线程的行号,所以它是线程私有(一个线程就有一个程序计数器)的。

相关文章:

JVM内存结构介绍

1. 什么是JVM 我们都知道在 Windows 系统上一个软件包装包是 exe 后缀的&#xff0c;而这个软件包在苹果的 Mac OSX 系统上是无法安装的。类似地&#xff0c;Mac OSX 系统上软件安装包则是 dmg 后缀&#xff0c;同样无法在 Windows 系统上安装。 Java 代码为什么可以在 Windows…...

Linux常见指令总结

ls&#xff1a;显示当前目录下文件列表 常用的命令行参数&#xff1a; -l 显示更多的文件属性 -a 显示所有的文件/目录&#xff08;包括隐藏的&#xff09; -d 只显示目录 ps&#xff1a;参数可以叠加使用。 例如&#xff1a;ls -la 显示所有文件…...

Day35-Linux网络管理5

Day35-Linux网络管理5 1. 网卡配置2. DNS客户端域名解析配置3. 给网卡配多个IP4. ip地址查看和设置4.1 ifconfig命令4.2 ip命令4.3 ip命令&#xff1a;查看和设置网络配置4.4 ip命令帮助 5. 路由5.1 路由功能分类&#xff1a;5.2 查看路由&#xff1a;5.3 路由表&#xff1a;5.…...

9个神奇免费AI编程助手,实现高效自动代码生成!

在AIGC技术工具快速发展的时代&#xff0c;对高效智能编程工具的需求和关注已达到空前的高度。本文将介绍9款免费且好用的AI编程助手工具。无论你是经验丰富的开发人员还是刚开始编程旅程的新手&#xff0c;这些AI代码软件都能帮助你提高项目开发的生产力、创造力和准确性&…...

Python 导入Excel三维坐标数据 生成三维曲面地形图(体) 5-3、线条平滑曲面且可通过面观察柱体变化(三)

环境和包: 环境 python:python-3.12.0-amd64包: matplotlib 3.8.2 pandas 2.1.4 openpyxl 3.1.2 scipy 1.12.0 代码: import pandas as pd import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D from scipy.interpolate import griddata fro…...

【CSP】2022–09-3 防疫大数据 100分 STL大模拟 使用map优化索引 有坑得注意

2022–09-3 防疫大数据 STL大模拟 使用map优化索引 2022–09-3 防疫大数据 STL大模拟 使用map优化索引基本思路遇到的问题&#xff08;学到的东西&#xff09;感悟完整代码 2022–09-3 防疫大数据 STL大模拟 使用map优化索引 这题中规中矩&#xff0c;不算太难也不算太简单&am…...

【Linux基础(三)】信号

学习分享 1、信号的基本概念2、查看信号列表3、常见信号名称4、signal库函数5、发送信号kill6、kill - signal &#xff08;无参信号&#xff09;示例6.1、kill - signal (不可靠信号)示例6.2、kill - signal (可靠信号)示例 7、信号分类7.1、信号运行原理分类7.2、信号是否携带…...

GEE图像可视化常用函数

目录 图层操作Map.addLayer&#xff08;&#xff09;Map.centerObject&#xff08;&#xff09; 直方图ui.Chart.image.histogram&#xff08;&#xff09; 时间序列统计ui.Chart.image.series&#xff08;&#xff09;ui.Chart.image.seriesByRegion&#xff08;&#xff09; …...

c++基础语法

文章目录 前言命名空间命名空间的使用 缺省参数缺省参数的使用 函数重载函数重载的作用函数重载的使用函数重载原理 引用引用的使用引用的使用场景引用和指针 extern Cinlineauto范围fornullptr 前言 大家好我是jiantaoyab&#xff0c;这篇文章给大家带来的是c语言没有的一些特…...

【工作实践-07】uniapp关于单位rpx坑

问题&#xff1a;在浏览器页面退出登录按钮上“退出登录”字样消失&#xff0c;而在手机端页面正常;通过查看浏览器页面的HTML代码&#xff0c;发现有“退出登录”这几个字&#xff0c;只不过由于样式问题&#xff0c;这几个字被挤到看不见了。 样式代码中有一行为&#xff1a…...

服务层组件

目录 连接层(Connection Pool) SQL接口(SQL Interface) 查询缓存(Caches&Buffers) Management Services&Utilities 查询分析器(Parser) 优化器(Optimizer)...

【学习笔记】VMware vSphere 6.7虚拟化入门

VMware vSphere 6.7虚拟化入门课程介绍 课程内容 1、VMware vSphere 6.7虚拟化入门课程介绍 2、ESXi6.7控制台设置 3、使用vSpkere Host client管理虚拟机 4、VMware EsXi基础操作 5、VMware Esxi存储管理 6、管理ESXi主机网络与虚拟机网络 7、安装配置vCenter Server Applia…...

如何防范企业内部安全威胁?

1 用户行为分析&#xff08;UEBA&#xff09; 现代化的用户行为分析产品具有多种优势功能&#xff0c;使企业能够有效地检测内部威胁。用户行为分析软件通过收集和分析来自各种来源的数据来分析和检测内部人员的可疑行为。这些来源包括网络日志和用户活动日志。通过检查这些数…...

内网渗透-跨域环境渗透-1

目录 smbclient工具 mimikatz工具 Kerbers协议 NTLM认证 hash传递攻击&#xff08;PTH攻击&#xff09; 黄金票据攻击 白银票据 MS14-068 smbclient工具 在linux里面连接远程windows共享目录&#xff0c;可以使用这个工具 ​ 第一种连接方式&#xff1a;smbclient -L 目…...

安信可IDE(AiThinker_IDE)编译ESP8266工程方法

0 工具准备 AiThinker_IDE.exe ESP8266工程源码 1 安信可IDE&#xff08;AiThinker_IDE&#xff09;编译ESP8266工程方法 1.1 解压ESP8266工程文件夹 我们这里使用的是NON-OS_SDK&#xff0c;将NON-OS_SDK中的1_UART文件夹解压到工作目录即可 我这里解压到了桌面&#xff0c…...

【java数据结构】HashMap和HashSet

目录 一.认识哈希表&#xff1a; 1.1什么是哈希表&#xff1f; 1.2哈希表的表示&#xff1a; 1.3常见哈希函数&#xff1a; 二.认识HashMap和HashSet: 2.1关于Map.Entry的说明:,> 2.2Map常用方法说明&#xff1a; 2.3HashMap的使用案例&#xff1a; 2.4Set常见方法…...

基于Springboot的高校汉服租赁网站(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的高校汉服租赁网站&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构…...

分布式解决方案

目录 1. 分布式ID1-1. 传统方案1-2. 分布式ID特点1-3. 实现方案1-4. 开源组件 2. 分布式Session2-1. 传统Session2-2. Spring-Session2-3. Token Redis2-4. JWT2-5. 拦截器统一处理Token2-6. Oauth2 3. 分布式锁3-1. redis3-2. Zookeeper 1. 分布式ID 1-1. 传统方案 时间戳U…...

力扣刷题日记——L724. 寻找数组的中心下标

1. 前言 今天是力扣刷题日记的第二天&#xff0c;今天依旧是一道简单题啊&#xff0c;慢慢来&#xff0c;先看看题目是什么吧。 2. 题目描述 给你一个整数数组 nums &#xff0c;请计算数组的 中心下标。 数组 中心下标 是数组的一个下标&#xff0c;其左侧所有元素相加的和…...

【Kotlin】类和对象

1 前言 Kotlin 是面向对象编程语言&#xff0c;与 Java 语言类似&#xff0c;都有类、对象、属性、构造函数、成员函数&#xff0c;都有封装、继承、多态三大特性&#xff0c;不同点如下。 Java 有静态&#xff08;static&#xff09;代码块&#xff0c;Kotlin 没有&#xff1…...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

【JVM】- 内存结构

引言 JVM&#xff1a;Java Virtual Machine 定义&#xff1a;Java虚拟机&#xff0c;Java二进制字节码的运行环境好处&#xff1a; 一次编写&#xff0c;到处运行自动内存管理&#xff0c;垃圾回收的功能数组下标越界检查&#xff08;会抛异常&#xff0c;不会覆盖到其他代码…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级

在互联网的快速发展中&#xff0c;高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司&#xff0c;近期做出了一个重大技术决策&#xff1a;弃用长期使用的 Nginx&#xff0c;转而采用其内部开发…...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

Go 语言并发编程基础:无缓冲与有缓冲通道

在上一章节中&#xff0c;我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道&#xff0c;它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好&#xff0…...

push [特殊字符] present

push &#x1f19a; present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中&#xff0c;push 和 present 是两种不同的视图控制器切换方式&#xff0c;它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...

NPOI操作EXCEL文件 ——CAD C# 二次开发

缺点:dll.版本容易加载错误。CAD加载插件时&#xff0c;没有加载所有类库。插件运行过程中用到某个类库&#xff0c;会从CAD的安装目录找&#xff0c;找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库&#xff0c;就用插件程序加载进…...

作为测试我们应该关注redis哪些方面

1、功能测试 数据结构操作&#xff1a;验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化&#xff1a;测试aof和aof持久化机制&#xff0c;确保数据在开启后正确恢复。 事务&#xff1a;检查事务的原子性和回滚机制。 发布订阅&#xff1a;确保消息正确传递。 2、性…...

Python 高效图像帧提取与视频编码:实战指南

Python 高效图像帧提取与视频编码:实战指南 在音视频处理领域,图像帧提取与视频编码是基础但极具挑战性的任务。Python 结合强大的第三方库(如 OpenCV、FFmpeg、PyAV),可以高效处理视频流,实现快速帧提取、压缩编码等关键功能。本文将深入介绍如何优化这些流程,提高处理…...