前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >jvm入门4:09方法区

jvm入门4:09方法区

原创
作者头像
用户10832809
发布2025-02-25 09:14:34
发布2025-02-25 09:14:34
940
举报

09 方法区

栈、堆、方法区的交互关系

方法区的理解

方法区可看作独立于堆的内存空间

1方法区域对一样,是各线程共享的内存区域;2在jvm启动时被创建,实际物理内存空间中和java堆区一样都是不连续的;3大小可选择固定或扩展;4方法区的大小决定了可以保存多少类,方法区溢出,虚拟机会报内存溢出错误,outofmemoryerror:pergen space、metaspace,如加载大量的第三方jar包,tomcat部署的工程过多30-50个,大量动态的生成反射类;关闭jvm就会释放这个区域的内存

方法区演进

由元空间替换永久代;2本质类似,都是jvm规范中方法区的实现,最大区别在于,元空间不再虚拟机设置的内存中,而使用本地内存;3内部结构调整很多;

设置方法区与OOM

1-X:MetaSpaceSize, MaxMetaSpaceSize,分别为初始分配空间和最大可分配空间;2window前者默认为21M,后者-1,无限制;3对于64位的服务器端jvm,默认初始值为21M,触及高位线,full gc触发,卸载没用的类(这些类对应的类加载器不再存活),高位线重置;避免多次调用full gc,这个水位线可设置的高一些

如何解决oom

1通过内存映像分析工具如memeory ananlyzer 对dump出来的堆转存储快照,确定内存中的对象是否必要的,分清楚到底是出现了内存泄漏leak,还是内存溢出overflow;

2内存泄漏,通过工具查看泄漏对象到gc roots的引用链,找到泄露对象是通过怎么样的路径与gc root关联,并导致垃圾收集器无法自动回收他们。掌握了泄漏对象的类型信息,gc roots引用链信息,可以比较准确的定位出泄漏代码的位置

3不存在内存泄漏,内存中的对象还必须存活着,应当检查虚拟机堆参数与物流内存是否可以调大,从代码上检查是否存在某些对象生命周期过长,持有状态时间过长的情况,尝试减少程序运行期的内存消耗

内存泄漏的定义

程序运行中持续申请内存却不释放不再使用的部分,导致可用内存不断减少。

内存泄漏的危害

- **性能降低**:可用内存减少,程序运行变慢,响应时间变长。

- **程序崩溃**:内存耗尽时,程序因无法获取足够内存而崩溃。

- **系统故障**:多个程序内存泄漏会耗尽系统资源,使系统死机、蓝屏,稳定性受影响。

内存泄漏的原因

- **动态内存未释放**:手动分配内存后使用完未释放。

- **对象引用复杂**:对象间相互引用使垃圾回收机制无法正常回收。

- **资源关闭问题**:使用文件、网络连接等资源后未正确关闭。

- **第三方问题**:第三方库或框架存在缺陷导致内存管理不当。

- **并发操作失误**:多线程或并发编程时,资源竞争和同步问题致内存管理混乱。

方法区内部结构

方法区存储内容:已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等

类型信息

对每个加载的类型(类class、接口Interface、枚举enum、注解annotation),jvm必须在方法区中存储以下类型信息,1这个类型的完整有效名称(全名=包名+类名);2这个类型直接父类的完整有效名(对于interface或object,没有父类);3这个类型的修饰符(pubuic、abstract、final的某个子集);4这个类型直接接口的一个有序列表;

域信息

1jvm必须在方法区中保存类型的所有域相关信息,以及域的声明顺序;2域的相关信息包括,域名称、域类型、域修饰符(public,private,protected,static,final,volatile,transient的某个子集)

方法信息

jvm必须保存所有方法的以下信息,同域信息一样包括声明顺序:1方法名称;2方法的返回类型(或void);3方法参数的数量和类型(按顺序);4方法的修饰符(public,private,protected,static,final,synchronized,native,abstract的一个子集);4方法的字节码(bytecodes)、操作数栈、局部变量表及大小(abstract和native方法除外);5异常表(native和asbtract除外),每个异常处理的开始位置,结束位置,代码处理在程序计数器中的偏移地址、被捕获异常类的常量池索引

non-final的类变量

1静态变量和类关联在一起,随着类的加载而加载,他们成为类数据在逻辑上的一部分;2类变量被类的所有实例共享,即时没有类实例也可以访问

全局变量

static,fianal,被声明为final的类变量处理方法不同,每个全局变量在编译的时候就分配了

运行时常量池vs常量池

1方法区内部包含了运行时常量池;2字节码文件,内部包含了常量池;3弄清楚方法区,需要理解清楚classfile,因为加载类的信息都在方法区;4弄清楚方法区的运行时常量池,需要理解classfile中的常量池

一个有效的字节码文件中,除了包含类的版本信息,字段,方法以及接口等描述信息外,还包含一项信息为常量池表,contstant pool table, 包括各种字面量和对类型、域、方法的符号引用

为什么需要常量池

一个java源文件在的类、接口,编译后会产生一个字节码文件。java中的字节码需要数据支持,通常在这种数据很大,以至于不能直接存到字节码里,换另一种方式,存到常量池,这个字节码包含了指向常量池的引用。在动态链接的时候会用到运行时常量池。

class文件很小,常量池中只保存了指向用到类的链接

常量池数据类型:数量值、字符串值、类引用、字段引用、方法引用

常量池,可以看作一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等类型

运行时常量池

1是方法区的一部分;2常量池表是class文件的一部分,用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中;3运行时常量池,在加载类和接口到虚拟机后,就会创建对应的运行时常量池

4运行时常量池中包含多种不同的常量,包括编译器就已经明确的数值字面量,运行期解析后才能获得的方法或字段引用。此时不再是常量池中的符号地址,换为真实地址。运行时常量池,相对于class文件常量池的一重要特征是具备动态性,String.intern();5运行时常量池类似于传统编程语言的符号表,但它所包含的数据却比符号表更丰富一些;6当创建类或接口的运行时常量池时,如果构造运行时常量池所需的内存空间超过了方法区所能提供的最大值,则jvm会抛出oom

方法区使用举例

方法区的垃圾回收

这个区域的回收效果难以令人满意,尤其是类型的卸载,条件相当苛刻。但这部分区域回收有时又是必要的。

方法区的垃圾收集主要回收两部分内容:常量池中废弃的常量和不再使用的类型

判定一个常量是否“废弃”比较简单,要判定一个类型是否属于“不再使用的类”苛刻,同时需要满足三个条件:1该类所有的实例已经被回收,java堆中不存在该类及其任何派生子类的实例;2加载该类的类加载器已经被回收,这个条件除非是经过精心设计的可替换类加载器的场景,如osgi、jsp的重新加载等,否则很难达成;3该类对应的class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

java虚拟机被允许满足上述三个条件的无用类进行回收,仅仅是被允许,而不是和对象一样,没有引用就必然会回收。关于是否要对类型进行回收,hotspot虚拟机提供了-xnoclassgic参数进行控制,还可使用-verbose:class以及-xx:traceclass-loading,-xx:+traceclassunloading查看类加载和卸载信息

有大量使用反射、动态代理、cglib等字节码框架,动态生成jsp,以及osgi这类频繁自定义类加载器的场景中,通常都需要java虚拟机具备类型卸载的能力,以保证不会对方法区造成过大的内存压力

面试题

jvm内存模型,都有哪些区,分别干什么

java8的内存分代改进

jvm内存分哪几个区,每个区的作用是什么

jvm内存分布/内存结构,栈和堆的区别,堆的结构,为什么两个surivivor区,eden和survivor的比例分配

jvm内存分区,为什么要有新生代和老年代

java内存分区

jvm运行时数据库区

什么时候对象会进入老年代

jvm的内存结构,eden和survivor比例

jvm内存为什么要分成新生代、老年代、持久代。新生代中为什么要分为eden和survivor

jvm内存模型以及分区,需要详细每个区放什么

jvm内存模型,java8做了什么修改

jvm内存分哪几个区,每个区的作用是什么

java内存分配

jvm的永久代会发生垃圾回收吗

jvm内存分区,为什么要有新生代和老年代

整理后面试题及精简回答:

1. **JVM内存模型的分区及作用**

答:程序计数器、虚拟机栈、本地方法栈、堆、方法区(Java 8元空间);分别管理指令、方法调用、本地方法、对象实例、类元数据。

2. **Java8内存分代改进**

答:移除永久代,方法区改为元空间(使用本地内存),避免OOM。

3. **堆的结构与分代原因**

答:堆分新生代(Eden+Survivor)和老年代;分代因对象生命周期差异,提高GC效率。

4. **Survivor区设计及比例(如8:1:1)**

答:两个Survivor避免内存碎片,复制算法优化;比例基于对象朝生夕死假设。

5. **栈和堆的区别**

答:栈存方法帧/局部变量(线程私有),堆存对象实例(线程共享)。

6. **对象何时进入老年代**

答:年龄超阈值、大对象、Survivor满时直接晋升。

7. **永久代(元空间)会垃圾回收吗?**

答:会,但条件严苛(如类卸载、常量池回收)。

---

合并说明:

- 同类问题合并为7个核心点,覆盖内存分区、Java8改进、分代逻辑、GC机制等高频考点。

- 每回答控制在30字内,突出关键术语(如元空间、复制算法)和核心逻辑(如分代优化GC)。

参考

康师傅jvm

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 09 方法区
    • 内存泄漏的定义
    • 方法区内部结构
    • 运行时常量池vs常量池
    • 方法区的垃圾回收
    • 面试题
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档