【个人学习】JVM(7):方法区概述、方法区内部结构、垃圾回收等
方法区
栈、堆、方法区的交互关系
从线程共享与否的角度来看
ThreadLocal:如何保证多个线程在并发环境下的安全性?典型场景就是数据库连接管理,以及会话管理。

栈、堆、方法区的交互关系
下面涉及了对象的访问定位
- Person 类的 .class 信息存放在方法区中
- person 变量存放在 Java 栈的局部变量表中
- 真正的 person 对象存放在 Java 堆中
- 在 person 对象中,有个指针指向方法区中的 person 类型数据,表明这个 person 对象是用方法区中的 Person 类 new 出来的

方法区的理解
官方文档:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5.4
方法区在哪里?
- 《Java虚拟机规范》中明确说明:尽管所有的方法区在逻辑上是属于堆的一部分,但一些简单的实现可能不会选择去进行垃圾收集或者进行压缩。但对于HotSpotJVM而言,方法区还有一个别名叫做Non-Heap(非堆),目的就是要和堆分开。
- 所以,方法区可以看作是一块独立于Java堆的内存空间。

方法区的基本理解
方法区主要存放的是 Class,而堆中主要存放的是实例化的对象
- 方法区(Method Area)与Java堆一样,是各个线程共享的内存区域。多个线程同时加载统一个类时,只能有一个线程能加载该类,其他线程只能等等待该线程加载完毕,然后直接使用该类,即类只能加载一次。
- 方法区在JVM启动的时候被创建,并且它的实际的物理内存空间中和Java堆区一样都可以是不连续的。
- 方法区的大小,跟堆空间一样,可以选择固定大小或者可扩展。
- 方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出,虚拟机同样会抛出内存溢出错误:
java.lang.OutofMemoryError:PermGen space
或者java.lang.OutOfMemoryError:Metaspace
- 加载大量的第三方的jar包
- Tomcat部署的工程过多(30~50个)
- 大量动态的生成反射类
- 关闭JVM就会释放这个区域的内存。
代码举例
public class MethodAreaDemo {public static void main(String[] args) {System.out.println("start...");try {Thread.sleep(1000000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("end...");}
}
简单的程序,加载了1600多个类

HotSpot方法区演进
- 在 JDK7 及以前,习惯上把方法区,称为永久代。JDK8开始,使用元空间取代了永久代。我们可以将方法区类比为Java中的接口,将永久代或元空间类比为Java中具体的实现类
- 本质上,方法区和永久代并不等价。仅是对Hotspot而言的可以看作等价。《Java虚拟机规范》对如何实现方法区,不做统一要求。例如:BEAJRockit / IBM J9 中不存在永久代的概念。
- 现在来看,当年使用永久代,不是好的idea。导致Java程序更容易OOm(超过-XX:MaxPermsize上限)
- 而到了JDK8,终于完全废弃了永久代的概念,改用与JRockit、J9一样在本地内存中实现的元空间(Metaspace)来代替
- 元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代最大的区别在于:元空间不在虚拟机设置的内存中,而是使用本地内存。
- 永久代、元空间二者并不只是名字变了,内部结构也调整了
- 根据《Java虚拟机规范》的规定,如果方法区无法满足新的内存分配需求时,将抛出OOM异常

设置方法区大小与 OOM
方法区的大小不必是固定的,JVM可以根据应用的需要动态调整。
JDK7及以前(永久代)
- 通过-XX:Permsize来设置永久代初始分配空间。默认值是20.75M
- -XX:MaxPermsize来设定永久代最大可分配空间。32位机器默认是64M,64位机器模式是82M
- 当JVM加载的类信息容量超过了这个值,会报异常OutofMemoryError:PermGen space。

JDK8及以后(元空间)
JDK8 版本设置元空间大小
-
元数据区大小可以使用参数 -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize 指定
-
默认值依赖于平台,Windows下,-XX:MetaspaceSize 约为21M,-XX:MaxMetaspaceSize的值是-1,即没有限制。
-
与永久代不同,如果不指定大小,默认情况下,虚拟机会耗尽所有的可用系统内存。如果元数据区发生溢出,虚拟机一样会抛出异常OutOfMemoryError:Metaspace
-
-XX:MetaspaceSize:设置初始的元空间大小。对于一个 64位 的服务器端 JVM 来说,其默认的 -XX:MetaspaceSize值为21MB。这就是初始的高水位线,一旦触及这个水位线,Full GC将会被触发并卸载没用的类(即这些类对应的类加载器不再存活),然后这个高水位线将会重置。新的高水位线的值取决于GC后释放了多少元空间。如果释放的空间不足,那么在不超过MaxMetaspaceSize时,适当提高该值。如果释放空间过多,则适当降低该值。
-
如果初始化的高水位线设置过低,上述高水位线调整情况会发生很多次。通过垃圾回收器的日志可以观察到Full GC多次调用。为了避免频繁地GC,建议将-XX:MetaspaceSize设置为一个相对较高的值。
方法区OOM
举例:
代码:OOMTest 类继承 ClassLoader 类,获得 defineClass() 方法,可自己进行类的加载
/*** jdk6/7中:* -XX:PermSize=10m -XX:MaxPermSize=10m** jdk8中:* -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m**/
public class OOMTest extends ClassLoader {public static void main(String[] args) {int j = 0;try {OOMTest test = new OOMTest();for (int i = 0; i < 10000; i++) {//创建ClassWriter对象,用于生成类的二进制字节码ClassWriter classWriter = new ClassWriter(0);//指明版本号,修饰符,类名,包名,父类,接口classWriter.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "Class" + i, null, "java/lang/Object", null);//返回byte[]byte[] code = classWriter.toByteArray();//类的加载test.defineClass("Class" + i, code, 0, code.length);//Class对象j++;}} finally {System.out.println(j);}}
}
不设置元空间的上限
使用默认的 JVM 参数,元空间不设置上限
输出结果:
10000
设置元空间的上限
JVM 参数
-XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m
输出结果:
8531
Exception in thread "main" java.lang.OutOfMemoryError: Metaspaceat java.lang.ClassLoader.defineClass1(Native Method)at java.lang.ClassLoader.defineClass(ClassLoader.java:763)at java.lang.ClassLoader.defineClass(ClassLoader.java:642)at com.atguigu.java.OOMTest.main(OOMTest.java:29)
如何解决OOM
这个属于调优的问题,这里先简单的说一下
- 要解决OOM异常或heap space的异常,一般的手段是首先通过内存映像分析工具(如Ec1ipse Memory Analyzer)对dump出来的堆转储快照进行分析,重点是确认内存中的对象是否是必要的,也就是要先分清楚到底是出现了内存泄漏(Memory Leak)还是内存溢出(Memory Overflow)
- 内存泄漏就是有大量的引用指向某些对象,但是这些对象以后不会使用了,但是因为它们还和GC ROOT有关联,所以导致以后这些对象也不会被回收,这就是内存泄漏的问题
- 如果是内存泄漏,可进一步通过工具查看泄漏对象到GC Roots的引用链。于是就能找到泄漏对象是通过怎样的路径与GC Roots相关联并导致垃圾收集器无法自动回收它们的。掌握了泄漏对象的类型信息,以及GC Roots引用链的信息,就可以比较准确地定位出泄漏代码的位置。
- 如果不存在内存泄漏,换句话说就是内存中的对象确实都还必须存活着,那就应当检查虚拟机的堆参数(-Xmx与-Xms),与机器物理内存对比看是否还可以调大,从代码上检查是否存在某些对象生命周期过长、持有状态时间过长的情况,尝试减少程序运行期的内存消耗。
方法区的内部结构
方法区存储什么?
概念

《深入理解Java虚拟机》书中对方法区(Method Area)存储内容描述如下:它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等。

类型信息
对每个加载的类型(类class、接口interface、枚举enum、注解annotation),JVM必须在方法区中存储以下类型信息:
- 这个类型的完整有效名称(全名=包名.类名)
- 这个类型直接父类的完整有效名(对于interface或是java.lang.Object,都没有父类)
- 这个类型的修饰符(public,abstract,final的某个子集)
- 这个类型直接接口的一个有序列表
域(Field)信息
也就是我们常说的成员变量,域信息是比较官方的称呼
-
JVM必须在方法区中保存类型的所有域的相关信息以及域的声明顺序。
-
域的相关信息包括:域名称,域类型,域修饰符(public,private,protected,static,final,volatile,transient的某个子集)
方法(Method)信息
JVM必须保存所有方法的以下信息,同域信息一样包括声明顺序:
- 方法名称
- 方法的返回类型(包括 void 返回类型),void 在 Java 中对应的为 void.class
- 方法参数的数量和类型(按顺序)
- 方法的修饰符(public,private,protected,static,final,synchronized,native,abstract的一个子集)
- 方法的字节码(bytecodes)、操作数栈、局部变量表及大小(abstract和native方法除外)
- 异常表(abstract和native方法除外),异常表记录每个异常处理的开始位置、结束位置、代码处理在程序计数器中的偏移地址、被捕获的异常类的常量池索引
举例
*** 测试方法区的内部构成*/
public class MethodInnerStrucTest extends Object implements Comparable<String>,Serializable {//属性public int num = 10;private static String str = "测试方法的内部结构";//构造器//方法public void test1(){int count = 20;System.out.println("count = " + count);}public static int test2(int cal){int result = 0;try {int value = 30;result = value / cal;} catch (Exception e) {e.printStackTrace();}return result;}@Overridepublic int compareTo(String o) {return 0;}
}
javap -v -p MethodInnerStrucTest.class > test.txt
- 反编译字节码文件,并输出值文本文件中,便于查看。参数 -p 确保能查看 private 权限类型的字段或方法
字节码:
Classfile /F:/IDEAWorkSpaceSourceCode/JVMDemo/out/production/chapter09/com/atguigu/java/MethodInnerStrucTest.classLast modified 2020-11-13; size 1626 bytesMD5 checksum 0d0fcb54854d4ce183063df985141ad0Compiled from "MethodInnerStrucTest.java"
//类型信息
public class com.atguigu.java.MethodInnerStrucTest extends java.lang.Object implements java.lang.Comparable<java.lang.String>, java.io.Serializableminor version: 0major version: 52flags: ACC_PUBLIC, ACC_SUPER
Constant pool:#1 = Methodref #18.#52 // java/lang/Object."<init>":()V#2 = Fieldref #17.#53 // com/atguigu/java/MethodInnerStrucTest.num:I#3 = Fieldref #54.#55 // java/lang/System.out:Ljava/io/PrintStream;#4 = Class #56 // java/lang/StringBuilder#5 = Methodref #4.#52 // java/lang/StringBuilder."<init>":()V#6
相关文章:

【个人学习】JVM(7):方法区概述、方法区内部结构、垃圾回收等
方法区 栈、堆、方法区的交互关系 从线程共享与否的角度来看 ThreadLocal:如何保证多个线程在并发环境下的安全性?典型场景就是数据库连接管理,以及会话管理。 栈、堆、方法区的交互关系 下面涉及了对象的访问定位 Person 类的 .class 信息存放在方法区中person 变量存放…...
@Scheduled 定时任务自定义
简介 Scheduled 定时任务自定义可以通过SchedulingConfigurer实现。 SchedulingConfigurer 是 Spring Framework 中的一个接口,用于配置定时任务。当你需要对定时任务进行更高级别的定制时,这个接口就显得非常有用。 可以通过SchedulingConfigurer 接口…...
一种新颖的面试方式
你好,我是 shengjk1,多年大厂经验,努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注!你会有如下收益: 了解大厂经验拥有和大厂相匹配的技术等 希望看什么,评论或者私信告诉我! 文章目录 一…...

【Linux】生产消费模型实践 --- 基于信号量的环形队列
你送出去的每颗糖都去了该去的地方, 其实地球是圆的, 你做的好事终会回到你身上。 --- 何炅 --- 基于信号量的环形队列 1 信号量2 框架构建3 代码实现4 测试运行 1 信号量 信号量本质是一个计数器,可以在初始化时对设置资源数量…...
Science Robotics 与蜜蜂群互动的蜂窝型机器人系统
蜜蜂,如黄蜂,蚂蚁和其他社会昆虫,建立大型自组织群体,通常被解释为自我调节的“超有机体”。这些超生物是生态系统的重要稳定剂,因此被认为是“关键物种”。例如,蜜蜂群落通过觅食授粉服务的生态效应对陆地…...
Vue 计算属性:优雅地处理数据逻辑
在 Vue.js 中,计算属性(Computed Properties)是一种非常实用的功能,它允许我们根据组件的响应式依赖进行缓存和派生状态。计算属性可以让我们以声明式的方式编写复杂的逻辑,而不必担心性能问题。 什么是计算属性&…...
C++中`union`
文章目录 C中的union什么是union?定义union示例一输出结果: 示例二修正后的代码解释输出结果结论 union的特性匿名union示例 union和struct的区别1. 内存布局2. 同时访问3. 用途 union和class的区别1. 数据成员2. 功能性3. 适用场景 在C编程中࿰…...

Linux——网络(1)
一、IPC(进程间通信方式) IPC:Inter Process Communication 共享内存(最高效的进程间通信方式) 虚拟地址 mmu(memory management unit ) 共享内存: 1.是一块,内核预留的空间 2.最高效的…...

【五】阿伟开始学Kafka
阿伟开始学Kafka 概述 人生若只如初见,阿伟心里回想起了第一次和Kafka见面的场景,记忆虽然已经有些模糊,但是感觉初次见面是美好的。积累了一些实战经验之后,阿伟感觉不能再是面对百度开发了,于是决心系统的学习一下Ka…...
Java—Arrays api
public static String toString(数组) //把数组拼接成一个字符串 public static int binarySearch(数组,查找的元素) //二分查找法查找元素 public static int[] copyOf(原数组,新数组长度) //拷贝数组 public st…...
Java - 基数排序算法介绍、应用场景和示例代码
概述 基数排序(Radix Sort)是一种非比较型整数排序算法,适用于整数或固定长度的字符串排序。它的基本思想是将待排序的元素分为多个关键字进行排序,通常从最低位(最低有效位,Least Significant Digit, LSD…...
Django 后端架构开发:文件云存储,从本地存储到腾讯COS桶集成
⭐ Django 后端架构开发:文件云存储,从本地存储到腾讯COS桶集成 目录 ☁️ 文件云存储 - 项目使用云存储💻 文件云存储 - 项目中使用本地存储📝 文件云存储 - 概述和创建项目🌐 腾讯COS桶 - 概述📚 腾讯CO…...
【系统分析师】-综合知识-计算机网络与信息安全
1、要对消息明文进行加密传送,当前通常使用的加密算法是 报文认证算法:数字摘要 RSA 非对称加密,一般不用于明文 MD5 数字摘要 SHA-1 数字摘要,160位的消息摘要 HMAC 以一个密钥和一个消息为输入,生成一个消息摘要作…...

C++ | Leetcode C++题解之第363题矩形区域不超过K的最大数值和
题目: 题解: class Solution { public:int maxSumSubmatrix(vector<vector<int>> &matrix, int k) {int ans INT_MIN;int m matrix.size(), n matrix[0].size();for (int i 0; i < m; i) { // 枚举上边界vector<int> sum(…...

python动画:场景的线性变换展示
一,主函数 LinearTransformationScene 是 Manim 中用于展示线性变换的场景类。它通过在一幅背景和前景平面上展示向量和变换,帮助理解线性代数中的概念。 LinearTransformationScene(include_background_planeTrue, include_foreground_planeTrue, ba…...

HBase体系架构与环境搭建
这里写目录标题 一、常见的NoSQL数据库二、HBase的体系架构和表结构三、搭建HBasa环境1.本地模式2.伪分布模式全分布模式HA模式 一、常见的NoSQL数据库 NoSQL数据库的说明与定义 NoSQL是一种不同于关系数据库的数据库管理系统设计方式,是对非关系型数据库的统称。它…...

海思SD3403/SS928V100开发(16)Tsensor驱动开发
1. 前言 由于需要检测SD3403芯片内部实时温度,需要开发Tsensor传感器驱动和应用 查看手册发现SD3403内部有三个Tsensor传感器 可以参考之前我写的35系列平台Tsensor驱动开发记录 海思35系列平台Tsensor驱动开发(1)驱动编写_t sensor-CSDN博客 海思35系列平台Tsensor驱动…...

JVM类加载机制—JVM类加载过程
一、概述 代码编译后,就会生成JVM(Java虚拟机)能够识别的二进制字节流文件(*.class)。而JVM把Class文件中的类描述数据从文件加载到内存,并对数据进行校验、转换解析、初始化,使这些数据最终成…...

可变参数模板与包装器
抱歉:铁汁们,最近在做兼职,积累社会经验,多有拖欠,请多多包涵(抱拳) 引子:接上回我们讲了C11的几种新增,今天就来接着讲C11中比较有用的二个东西可变参数模板与包装器。…...

工业控制常用“对象“数据类型汇总(数据结构篇)
合理巧妙的数据结构会大大简化项目的编程工作量,所以任何项目前期第一步应该是设计巧妙的数据结构、封装对象属性。这样会使我们的编程快捷和高效。这篇博客作为数据类型汇总,会不间断更新。 1、普通电机轴对象 2、普通电机轴对象(详细结构变量) TYPE "udtMotorAxis&q…...
浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)
✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...

centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...

【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...
基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解
JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用,结合SQLite数据库实现联系人管理功能,并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能,同时可以最小化到系统…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...
第7篇:中间件全链路监控与 SQL 性能分析实践
7.1 章节导读 在构建数据库中间件的过程中,可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中,必须做到: 🔍 追踪每一条 SQL 的生命周期(从入口到数据库执行)&#…...
虚幻基础:角色旋转
能帮到你的话,就给个赞吧 😘 文章目录 移动组件使用控制器所需旋转:组件 使用 控制器旋转将旋转朝向运动:组件 使用 移动方向旋转 控制器旋转和移动旋转 缺点移动旋转:必须移动才能旋转,不移动不旋转控制器…...
计算机系统结构复习-名词解释2
1.定向:在某条指令产生计算结果之前,其他指令并不真正立即需要该计算结果,如果能够将该计算结果从其产生的地方直接送到其他指令中需要它的地方,那么就可以避免停顿。 2.多级存储层次:由若干个采用不同实现技术的存储…...
用 FFmpeg 实现 RTMP 推流直播
RTMP(Real-Time Messaging Protocol) 是直播行业中常用的传输协议。 一般来说,直播服务商会给你: ✅ 一个 RTMP 推流地址(你推视频上去) ✅ 一个 HLS 或 FLV 拉流地址(观众观看用)…...