如题,此文的内容是基于 SUN JVM HotSpot 。
首先,简单说下JVM的体系结构,JVM 主要分为三个系统:类加载器子系统、运行时数据区、执行引擎,如下图:

本文要讲的 「内存结构」 属于 「运行时数据区」。
在网上找到一张 1.6、1.7、1.8 的演进过程(原图出处: https://www.jianshu.com/p/8d24230767a4 )。觉得还是很清晰的,这里那里引用下:)。

依据上图的内容,我又梳理了一个差异表格,如下:

通过上边表格我们可以看出来,1.6、1.7、1.8 这三个版本,变化最大的是 「方法区」 和 「元空间」。1.8 用 「元空间」 取代了 「方法区(永久代)」,如下图:

在 1.8 中,内存结构主要由三大块组成:堆内存、元空间、栈。这里堆内存占据了最大的一块儿区域,Ta 由 新生代 和 老年代 组成,分别占据了堆内存空间的 1/3 和 2/3 。而 新生代 又分为三个部分,Eden、From Survivor、To Survivor,他们占据新生代空间的比例为 8:1:1(可调节)。

我们可以通过以下几个参数来对 堆内存(Heap) 的空间进行设置,如下:
| 参数名 | 描述 |
|---|---|
| -Xms | JVM初始分配的内存,默认是物理内存的1/64 |
| -Xmx | JVM最大分配的内存,默认是物理内存的1/4 |
| -Xmn | JVM新生代分配的内存,默认是堆内存的1/3 |
在 JVM 的参数中没有专门用来设置 老年代 大小的,但是我们可以通过公式 -Xmx = -Xmn + 老年代 这个公式,来得知 -Xmx 减去 -Xmn 就是老年代的大小了。
默认空余堆内存小于 40% 时,JVM 就会增大堆直到 -Xmx 的最大限制;空余堆内存大于 70% 时,JVM 会减小堆直到 -Xms 的最小限制。所以服务器通常设置 -Xms 与 -Xmx 相等以免在每次 GC 后调整堆的大小。
在 Java 虚拟机中,方法区是一块儿可供 各线程共享 的 运行时数据区。不同的 JVM 版本,方法区中存储的数据略有不同(详见上图)。
方法区 作为一个概念在 《Java虚拟机规范》只是规定它的作用,但是并没有规定怎么去实现 Ta。这样就造成了各个 JVM 厂家,对自家的 JVM 中的 方法区 的实现也各不相同。
在《Java虚拟机规范》中 Ta 被描述为 堆(Heap) 的一个逻辑部分,Ta 还有一个别名叫 Non-Heap(非堆),这是为了与 Java Heap 来做区分。
这里的 永久代 其实就是 Sun 推出的 JVM 产品 HotSpot 对 方法区 的实现,其他虚拟机实现并没有 永久代 这一概念,也就是说 永久代 是 HotSpot 的专属概念,例如: JRockit(Oracle)、J9(IBM) 中就没有 永久代。
方法区 和 永久代 的关系,和 Java 中的 接口 和 类 类似(接口 = 方法区;类 = 永久代),永久代 只是 方法区 这个 接口 的一个实现而已。
我们可以通过以下 JVM 参数来调节方法区的大小:
| 参数名 | 描述 |
|---|---|
| -XX:PermSize | 初始空间大小 |
| -XX:MaxPermSize | 最大空间,超过这个值将会抛出 OutOfMemoryError 异常 java.lang.OutOfMemoryError: PermGen |
误解:
网络上很多文章有这样一个说法:
通过设置 -XX:PermSize 和 -XX:MaxPermSize 可以调节 非堆(Non-Heap) 内存的大小 。
说可以调节 非堆(Non-heap) 内存的大小,其实是不准确的。严格说来这两个参数只对调节 永久代(HotSpot 对方法区的实现) 的大小有效。之所以这么说,是因为以下两点:
综上所述,我的理解是 -XX:PermSize 和 -XX:MaxPermSize 与 永久代 是一体的,同生同死的关系。
最后,还有一个比较有迷惑性的问题:
永久代 真的会像名字一样永远都不会进行垃圾收集吗?
答案是否定的。首先,HotSpot 设计团队之所以用 永生代 来实现 方法区,就是为了要复用HotSpot 的垃圾收集器,这样 GC分代收集 就可以扩展到方法区了。永久代不代表永远不进行垃圾收集,只是因为这个区域内的回收结果很难令人满意,尤其是类的卸载,条件特别的苛刻。
元空间 这个概念是在 JDK1.8 HotSpot JVM 中出现的,Ta 作为 方法区 的另一个 实现 取代了之前的 永久代。
元空间 与 永久代 的最大的不同是:元空间 并不在虚拟机中,Ta 使用的是 本地内存。因此,默认情况下,元空间 的大小仅受 本地内存限制,但我们可以通过对以下 JVM 参数的设置来调整 元空间 的大小:
| 参数名 | 描述 |
|---|---|
| -XX:MetaspaceSize | 初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时 GC 会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过 MaxMetaspaceSize 时,适当提高该值。 |
| -XX:MaxMetaspaceSize | 最大空间,默认是没有限制的。 |
除了上面两个指定大小的参数以外,还有两个与 GC 相关的参数:
| 参数名 | 描述 |
|---|---|
| -XX:MinMetaspaceFreeRatio | 在 GC 之后,最小的 Metaspace 剩余空间容量的百分比,减少为分配空间所导致的垃圾收集 |
| -XX:MaxMetaspaceFreeRatio | 在 GC 之后,最大的 Metaspace 剩余空间容量的百分比,减少为释放空间所导致的垃圾收集 |