【JVM】- 内存结构

引言

JVM:Java Virtual Machine

  • 定义:Java虚拟机,Java二进制字节码的运行环境
  • 好处
    • 一次编写,到处运行
    • 自动内存管理,垃圾回收的功能
    • 数组下标越界检查(会抛异常,不会覆盖到其他代码)
    • 多态
  • 比较
    在这里插入图片描述

内存结构

程序计数器(Program Counter Register)

作用:记住下一条JVM的机制
特点
- 线程私有的
- 不会存在内存溢出

虚拟机栈

定义

  • 每个线程运行所需要的内存;
  • 每个栈由多个栈帧组成,对应每次方法调用所占的内存;
  • 每个线程只有一个活动栈帧,对应每次方法调用所占用的内存

垃圾回收只设计堆内存中的无用对象,不涉及栈内存。
栈内存的分配可以通过-Xss来设置,并不是越大越好。
方法内的局部变量不一定是线程安全的:

  • 如果这个局部变量没有逃离方法的作用范围,则他是线程安全的
  • 如果局部变量引用了对象,并逃离了方法的作用范围,则需要考虑线程安全

栈内存溢出

  • 栈帧过多导致栈内存溢出:递归调用
  • 栈帧过大导致栈内存溢出

本地方法栈

本地方法使用的内存就是本地方法栈(由C、C++代码写的方法)

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

特点

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

前面的程序计数器虚拟机栈本地方法栈都是每个线程独有的,但是堆是线程共享的
可以使用-Xmx参数来设置堆内存大小
可以使用jconsole工具来查看堆内存占用情况

方法区

在这里插入图片描述

  • 共享性:方法区是线程共享的内存区域
  • 逻辑部分:方法区是JVM规范中的逻辑概念,具体实现因JVM版本而异
  • 存储:和类相关的信息(成员方法、构造器、成员变量)、运行时常量池、静态变量、方法字节码、字段和方法信息

内存溢出

  • 1.8以前导致永久代内存溢出(1.8以前,方法区由永久代实现,位于堆中)PermGen space
  • 1.8之后导致元空间内存溢出(1.8以后,方法区由元空间实现,使用本地内存)Metaspace

常量池:一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息
运行时常量池:常量池是.class文件中的,当类被加载时,他的常量池信息就会被放入运行时常量池,并把里面的符号地址变为真实地址。

StringTable

常量池中的信息,都会被加载到运行时常量池中。

  • 常量池中的字符串只是符号,第一次用到时才会变成对象(懒加载)
  • 利用运行时常量池的机制,来避免重复创建字符串对象
  • 字符串变量拼接的原理是StringBuilder(1.8、在中)
  • 字符串常量拼接的原理是编译器优化(在编译期如果确定字符串,就把它放入常量池中)
  • 可以使用intern方法,主动将常量池中还没有的字符串对象放入串池中
    • 1.8:将这个字符串对象尝试放入串池,如果有则不会放入;如果没有则放入串池,会把串池中的对象返回
    • 1.6:会把这个字符串对象尝试放入串池,如果有则不会放入;如果没有会把此对象复制一份,放入串池,会把串池中的对象返回(调用intern()方法创建的对象和放入串池的对象是两个对象)
public class Main {public static void main(String[] args) {/*StringTable ["a", "b", "ab"](hashtable结构,不能扩容)s1、s2、s3:在常量池中s4:new String("ab")*/String s1 = "a", s2 = "b", s3 = "ab"; // 懒加载:用到了才会把对应的字符串对象放入常量池中String s4 = s1 + s2; // 在堆中System.out.println(s3 == s4); // false/*s5:在编译期间已经确定结果为"ab",所以直接从常量池中取即可*/String s5 = "a" + "b"; // 在常量池中System.out.println(s3 == s5); // true/*x1.intern():将字符串对象尝试放入串池中,如果有则不会放入;如果没有,则放入串池,并返回串池对象*/String x1 = new String("c") + new String("d"); // 变量动态拼接,此时x1在堆中;"c"、"d"会在串池中String x2 = x1.intern();System.out.println("cd" == x1); // trueSystem.out.println("cd" == x2); // true/*String str1 = "cd";【串池】:["cd"]本来str2是在堆中,"c", "d"放入串池:String str2 = new String("c") + new String("d");【串池】:["cd", "c", "d"]使用str2.intern()方法,会将str2尝试放入串池【串池】:["cd", "c", "d"]将"cd"放入串池时,发现串池中已经存在该对象,"cd"对象放入失败*/String str1 = "cd";String str2 = new String("c") + new String("d");str2.intern();System.out.println(str1 == str2); // false}
}

StringTable位置

在这里插入图片描述

  • JDK1.6:
    • 运行时常量池位于永久代中(方法区)
    • 字符串常量池位于永久代中(方法区)
  • JDK1.8:
    • 运行时常量池位于元空间中(方法区)
    • 字符串常量池位于堆中(不在方法区里了)

由于永久代的Full GC触发时机是:永久代的空间不足才会触发,就会导致StringTable的回收时机并不会很频繁,但是StringTable又是一个需要被频繁使用的,这样很容易就会导致永久代空间不足。所以JDK8才把String Table存放位置改堆中。

StringTable性能调优

本质:调整HashTable中的桶个数
通过-XX:StringTableSize=200000调整HashTable中的桶个数

如果系统中字符串常量的个数较多,可以适当的调整HashTable中的桶大小,减小hash冲突。

案例】:某平台要存储用户大量的信息,需要存储大量的用户信息,但是用户的地址信息大部分都可能是重复的,如果不加以区分,直接把这么多重复的地址信息全部存入内存,那么会占用大量的堆内存。它的解决方法就是采用字符串的intern()方法,这样就可以去除重复的地址,相同的地址只会在串池中存储一份,这样也能减少字符串对于内存的占用。

结论】:如果应用里有大量的重复的字符串,可以考虑使用字符串的intern()方法,将这些字符串全部放入常量池中,这样可以避免这些字符串重复存储。

直接内存(操作系统的内存,不属于java虚拟机)

  • 常见于NIO操作,用于数据缓冲区
  • 回收分配成本高,读写性能高
    在这里插入图片描述

java程序每次从磁盘文件中读取,都需要先读取到系统内存,再读取到java堆内存,这样会造成很多不必要的浪费。

改进】:划出一块内存区域(direct memory),在这块内存区域中,java代码和系统内存都可以直接访问。从磁盘文件中读取后把数据放入直接内存,java代码也可以访问到这个直接内存,这样就会少了一次缓冲区的赋值操作。
在这里插入图片描述

  • 不受JVM内存回收管理,垃圾回收并不会导致直接内存释放,因为直接内存是操作系统的内存,不属于java虚拟机。
    • 使用Unsafe对象完成直接内存的分配回收,并且回收需要主动调用freeMemory方法
    • ByteBuffer的实现类内部,使用了Cleaner(虚引用)来检测ByteBuffer对象,一旦ByteBuffer对象被垃圾回收,那么就会有ReferenceHandler线程通过Cleaner的clean方法调用freeMemory来释放直接内存。

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

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

相关文章

React 基础入门笔记

一、JSX语法规则 1. 定义虚拟DOM时,不要写引号 2.标签中混入JS表达式时要用 {} (1).JS表达式与JS语句(代码)的区别 (2).使用案例 3.样式的类名指定不要用class,要用className 4.内…

Linux链表操作全解析

Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…

SQL进阶之旅 Day 19:统计信息与优化器提示

【SQL进阶之旅 Day 19】统计信息与优化器提示 文章简述 在数据库性能调优中,统计信息和优化器提示是两个至关重要的工具。统计信息帮助数据库优化器评估查询成本并选择最佳执行计划,而优化器提示则允许开发人员对优化器的行为进行微调。本文深入探讨了…

安宝特方案丨船舶智造AR+AI+作业标准化管理系统解决方案(维保)

船舶维保管理现状:设备维保主要由维修人员负责,根据设备运行状况和维护计划进行定期保养和故障维修。维修人员凭借经验判断设备故障原因,制定维修方案。 一、痛点与需求 1 Arbigtec 人工经验限制维修效率: 复杂设备故障的诊断和…

MFC内存泄露

1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…

基于区块链的供应链溯源系统:构建与实践

前言 在当今全球化的经济环境中,供应链的复杂性不断增加,商品从原材料采购到最终交付给消费者的过程涉及多个环节和众多参与者。如何确保供应链的透明度、可追溯性和安全性,成为企业和消费者关注的焦点。区块链技术以其去中心化、不可篡改和透…

Web攻防-SQL注入数据格式参数类型JSONXML编码加密符号闭合

知识点: 1、Web攻防-SQL注入-参数类型&参数格式 2、Web攻防-SQL注入-XML&JSON&BASE64等 3、Web攻防-SQL注入-数字字符搜索等符号绕过 案例说明: 在应用中,存在参数值为数字,字符时,符号的介入&#xff0c…

探秘鸿蒙 HarmonyOS NEXT:实战用 CodeGenie 构建鸿蒙应用页面

在开发鸿蒙应用时,你是否也曾为一个页面的布局反复调整?是否还在为查 API、写模板代码而浪费大量时间?今天带大家实战体验一下鸿蒙官方的 AI 编程助手——CodeGenie(代码精灵) ,如何从 0 到 1 快速构建一个…

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…

使用Whisper本地部署实现香港版粤语+英语混合语音转文字方案

今天要一个非常好的朋友有个工作,就是要把医院医生诊断的说话记录转成文字,之前都是她本人一句一句的听,然后记录下来的,我想通过ai 来解决这个问题。 她的需求如下: 不能把数据传到网上,隐私问题所以需要…

案例分享--汽车制动卡钳DIC测量

制动系统是汽车的主要组成部分,是汽车的主要安全部件之一。随着车辆性能的不断提高,车速不断提升,对车辆的制动系统也随之提出了更高要求,因此了解车辆制动系统中每个部件的动态行为成为了制动系统优化的主要途径,同时…

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…

VB.net复制Ntag213卡写入UID

本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …

SQL SERVER 数据库迁移的三种方法!

要将SQL Server从研发环境的把数据库结构(不含数据)迁移至生产环境,可通过以下几种方法实现。以下是具体操作步骤及适用场景: ⚙️ 一、使用SSMS图形界面生成结构脚本(推荐新手) 通过SQL Server Management Studio的生成脚本向导,仅导出数据库架构: ​​连接测试库​​…

C# 快速检测 PDF 是否加密,并验证正确密码

引言:为什么需要检测PDF加密状态? 在批量文档处理系统(如 OCR 文字识别、内容提取、格式转换)中,加密 PDF 无法直接操作。检测加密状态可提前筛选文件,避免流程因密码验证失败而中断。 本文使用 Free Spire…

(33)课54:3 张表的 join-on 连接举例,多表查询总结。数据库编程补述及游标综合例题。静态 sqL与动态sqL(可带参数)

(112)3 张表的 join-on 连接举例 : (113) 多表查询总结 : (114)数据库编程补述 : 综合例题 : 以上没有动手练习,不知道这样的语法是否…

再见 Navicat!一款开源的 Web 数据库管理工具!

大家好,我是 Java陈序员。 在日常的开发工作中,常常需要与各种数据库打交道。而为了提高工作效率,常常会使用一些可视化工具进行操作数据库。 今天,给大家介绍一款开源的数据库管理工具,无需下载安装软件&#xff0c…

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…

函数中的Callable

在编程中,​Callable(可调用对象)​​ 是指任何可以通过 () 操作符调用的对象。在函数和类设计的上下文中,Callable 通常指代可以被调用的实体,例如函数、方法、Lambda表达式或实现了 __call__ 方法的对象。以下是详细…