对JVM虚拟机中方法区的理解

时间:2023-03-08 18:21:38

因为jdk8的jvm已经取消了方法区,所以这边先主要介绍jdk8以下版本中方法区相关内容。

1.虚拟机规范中方法区的概念:

原文链接:http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-2.html#jvms-2.5.4

The method area is analogous to the storage area for compiled code of a conventional language or analogous to the "text" segment in an operating system process.
It stores per-class structures such as the run-time constant pool, field and method data, and the code for methods and constructors, including the special methods (§2.9) used in class and instance initialization and interface initialization.
翻译:JVM有一个所有线程共享的方法区。方法区类似于一个常规编程语言编译后代码的存储区域,或者操作系统进程中的代码段。它存储每个类的结构,比如运行时常量池,字段和方法数据,以及方法和构造体的代码,包括在类和实例初始化、接口初始化过程中用到的特殊方法(比如:<init>,<clinit>等,可参考原文链接) The method area is created on virtual machine start-up. Although the method area is logically part of the heap, simple implementations may choose not to either garbage collect or compact it. This version of the Java Virtual Machine specification does not mandate the location of the method area or the policies used to manage compiled code. The method area may be of a fixed size or may be expanded as required by the computation and may be contracted if a larger method area becomes unnecessary. The memory for the method area does not need to be contiguous.
翻译:方法区在虚拟机启动时创建。尽管方法区在逻辑上是堆的一部分,JVM的简单实现也许选择既不进行垃圾收集,也不进行整理。这个版本的虚拟机规范不强制方法区的位置以及用来管理编译后代码的策略。方法区可能固定大小,也可能按计算需要进行扩展以及当更大的方法区变得没必要时进行收缩。方法区的内存不需要是连续的。
A Java Virtual Machine implementation may provide the programmer or the user control over the initial size of the method area, as well as, in the case of a varying-size method area, control over the maximum and minimum method area size.
JVM实现可能提供给程序员或者用户对方法区初始大小的控制,以及,在可变容量情况下,对最大和最小容量的控制。
The following exceptional condition is associated with the method area: If memory in the method area cannot be made available to satisfy an allocation request, the Java Virtual Machine throws an OutOfMemoryError.
与方法区关联的异常情况:
如果方法区的内存不能满足一次分配内存的请求,虚拟机抛出OOM错误。

大概能了解到:

方法区主要用于存放Class相关的信息,如运行时常量池,字段和方法数据,以及方法和构造体的代码,包括在类和实例初始化、接口初始化过程中用到的特殊方法(比如:<init>,<clinit>等)。

2.java虚拟机:JVM高级特性与最佳实践中的定义:

方法区( Method Area) 与Java堆一样, 是各个线程共享的内存区域, 它用于存储已被虚拟机加载的类信息、
常量、 静态变量、 即时编译器编译后的代码等数据。 虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分, 但是
它却有一个别名叫做Non-Heap( 非堆) , 目的应该是与Java堆区分开来。
对于习惯在HotSpot虚拟机上开发、 部署程序的开发者来说, 很多人都更愿意把方法区称为“ 永久
代” ( Permanent Generation) , 本质上两者并不等价, 仅仅是因为HotSpot虚拟机的设计团队选择把GC分代收集
扩展至方法区, 或者说使用永久代来实现方法区而已, 这样HotSpot的垃圾收集器可以像管理Java堆一样管理这部
分内存, 能够省去专门为方法区编写内存管理代码的工作。 对于其他虚拟机( 如BEA JRockit、 IBM J9等) 来说是
不存在永久代的概念的。 原则上, 如何实现方法区属于虚拟机实现细节, 不受虚拟机规范约束, 但使用永久代来实
现方法区, 现在看来并不是一个好主意, 因为这样更容易遇到内存溢出问题( 永久代有-XX: MaxPermSize的上
限, J9和JRockit只要没有触碰到进程可用内存的上限, 例如32位系统中的4GB, 就不会出现问题) , 而且有极少数
方法( 例如String.intern( ) ) 会因这个原因导致不同虚拟机下有不同的表现。 因此, 对于HotSpot虚拟机, 根据
官方发布的路线图信息, 现在也有放弃永久代并逐步改为采用Native Memory来实现方法区的规划了[1], 在目前已
经发布的JDK 1.7的HotSpot中, 已经把原本放在永久代的字符串常量池移出。
Java虚拟机规范对方法区的限制非常宽松, 除了和Java堆一样不需要连续的内存和可以选择固定大小或者可扩
展外, 还可以选择不实现垃圾收集。 相对而言, 垃圾收集行为在这个区域是比较少出现的, 但并非数据进入了方法
区就如永久代的名字一样“ 永久” 存在了。 这区域的内存回收目标主要是针对常量池的回收和对类型的卸载, 一般
来说, 这个区域的回收“ 成绩” 比较难以令人满意, 尤其是类型的卸载, 条件相当苛刻, 但是这部分区域的回收确
实是必要的。 在Sun公司的BUG列表中, 曾出现过的若干个严重的BUG就是由于低版本的HotSpot虚拟机对此区域未完
全回收而导致内存泄漏。
根据Java虚拟机规范的规定, 当方法区无法满足内存分配需求时, 将抛出OutOfMemoryError异常

3.永久代和方法区的关系

永久代常常不和方法区一在同一个层面来提起,和它并列的词语一般是:新生代和老年代。

提到新生代(因为通常采用标记-复制算法,常又分为Eden和两个Survivor)、老年代、永久代,我们一般想到的是分代收集的垃圾收集算法。

比如新生代常用标记-复制算法,老年代常用标记-清理或者标记-整理,而永久代呢,是HotSpot虚拟机实现方法区的地方。

这个区域(方法区),在虚拟机规范中,是不要求有垃圾收集的,只是HotSpot虚拟机仍然对方法区所处的永久代进行了垃圾回收,

主要是无用的字符常量或者Class信息,虽然这部分数据进行回收的条件很苛刻,不过还是有的。

具体也可以参考下面的链接:

https://www.zhihu.com/question/49044988