❕ 方法区看作是堆的逻辑组成部分,其主要存储类信息,常量池,静态变量,JIT 编译后的代码等数据。是独立于 Java 堆的内存空间。对比堆区来理解,堆存放的是对象,而方法区则存的就是类的元数据。元数据不是类的 Class 对象,Class 对象是加载的最终产品,而方法代码,变量名,方法名,访问权限,返回值等都是在方法区的。
基本认识:
- 与堆一样,各个线程共享。
- 在启动 JVM 时创建,并且其实际的物理内存空间和 Java 堆区一样,可以为不连续的。
- 可以选择固定大小或者可扩展。
- 方法区的大小决定了系统可以保存多少个类,若太多类,就会导致方法区的溢出,会抛出java.lang.OutOfMemoryError:PermGen space 或者 java.lang.OutOfMemoryError:Metaspace 错误。
- 关闭 Jvm 会进行内存的释放。
- 方法区(method area)只是 JVM 规范中定义的一个概念,用于存储类信息、常量池、静态变量、JIT编译后的代码等数据,并没有规定如何去实现它,不同的厂商有不同的实现。而永久代(PermGen)是 Hotspot 虚拟机特有的概念, Java8 的时候又被元空间取代了,永久代和元空间都可以理解为方法区的落地实现。
History
JDK 7 及以前,方法区在习惯上称之为永久代。从 8 开始,使用元空间取代了永久代。
本质上永久代和方法区并不等价。
我们可以通过使用 XX:PermSize 来设置永久代初始分配空间 XX:MaxPermSize 来设定永久代最大可分配空间
XX:MetaspaceSize 和 -XX:MaxMetaspaceSize 来指定元数据大小
需要注意的是我们应该为 XX:MetaSpaceSize 设置为一个相对较高的值,因为这就是元空间的初始高水位线,如果设置的太低就会引发频繁的 Full GC ☠️。
OOM
一旦 JVM 加载的类信息容量超过了方法区设置的值(MaxPermSize)就会引发 OOM
解决思路:
先通过内存映射工具对 dump 出的堆存储快照进行分析,确认内存中的对象是否是必要的,以分清是内存泄漏还是内存溢出。
内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出。 内存泄漏是指你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果你申请到的那块内存你自己也不能再访问(也许你把它的地址给弄丢了),而系统也不能再次将它分配给需要的程序
若泄漏,进一步通过工具查看泄漏对象到 GC Roots 的应用链。就能找到泄漏的对象是通过怎么样的路径与 GC Roots相关联并导致垃圾收集器无法自动回收。
如果非内存泄漏,就要检查虚拟机的堆参数(-Xms 与 -Xms),与机器物理内存对比看是否可以调大,从代码上检查是否存在某些对象生命周期过长,持有状态广场,尝试减少运行期的内存消耗。
我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!