这是「死磕P7」系列第 001 篇文章,欢迎大家来跟我一起 死磕 100 天,争取在 2025 年来临之际,给自己一个交代。
JVM 内存区域划分是面试常考点,属于死记硬背型,比较让人头大的是不同版本的 JDK 具有不同的划分方式,主要矛盾点就来自 方法区,永久代,元空间 这几个破玩意。
整理了一些个人觉得比较靠谱的资料,进行了一些汇总,第一方便自己记忆,第二也希望大家不要再为内存区域划分的面试而苦恼。
虚拟机规范定义
先来看一下《Java 虚拟机规范》定义的概念,规范和具体实现是两回事,可以理解为规范是接口定义,具体的虚拟机实现是接口的实现类,实现细节千差万别,如 HotSpot VM, JRockit VM, Graal VM 等等(这些知道一下就好)。
程序计数器,虚拟机栈,本地方法栈基本没啥争议点,也比较好记,并且他们是属于线程隔离的(线程独享,一人一杯奶茶,各喝各的)
主要容易搞混的是 方法区 和 堆,究其原因其实就是因为版本更新导致的,一般大家说的都是 HotSpot VM, 下面用图示的方式告诉您不同版本的区别是什么。
JDK 1.6 内存划分
JDK 1.6 JVM 运行时内存就分为方法区(也叫永久代),堆,虚拟机栈,本地方法栈,程序计数器
JDK 1.7 内存划分
JDK 1.7 中将方法区中的字符串常量池和静态变量等存储区域调整到了堆中,方法区中只保留了类型信息,划分区域仍然是方法区(也叫永久代),堆,虚拟机栈,本地方法栈,程序计数器,唯一的区别就是方法区里面放置的内容减少了,如下图
JDK 1.8 内存划分
JDK 1.8 中将方法区直接移出了 JVM 内存,直接放置到了 物理内存 中,并且改名叫元空间(Meta Space),也就没有了永久代的叫法(当然,你要称元空间就是永久代也不算错,就好比你小时候一直被大家叫:狗蛋,长大了回老家,老同学还这么叫你一样,哈哈)
为什么做这些改动?
大概了解一点就行,我们又不是设计开发 JVM 的,别人给我们啥就用啥好了!
原因大概有以下几点:
-
Oracle 收购了两种 JVM:HotSpot VM 和 JRockit VM,并且想要将它们整合,但二者方法区实现差异较大;
-
字符串存在永久代中,容易出现性能问题和 OOM;
-
类及方法的信息大小较难确定,永久代大小难以确定:太小易导致永久代溢出,太大则易导致老年代溢出(JVM 内存是有限的,此消彼长);
-
永久代会为垃圾回收带来不必要的复杂度,且回收效率较低(性价比低)。
总结
JVM 内存划分一直都是考点,当然考得不仅仅是上面简单的怎么划分的,今天主要让大家彻底搞懂不同版本 JVM 之间的划分差异,做到了然于胸,不再为网上的乱七八糟的文章所困惑(感觉大家写的又对又不对,比较乱)。
下一篇我们会继续解读 JVM 的内存划分,主要介绍各区域的一些特点。
如果大家对技术感兴趣,欢迎大家链接我,一起拉个小群技术交流,一起成长。
小福利
文末小福利,作为资深囤货达人,购置或转存了上千 T 的各种资源,反正我也学不完,如有需要,可以公号: 新质程序猿 找到我,直接送您,能帮助到大家也算是有所福报吧!