【面试题】JVM篇

【面试题】JVM篇
時光内容来源于网络,如有侵权请联系删除
为什么 Java 8 移除了永久代(PermGen)并引入了元空间(Metaspace)?
Java 8 移除永久代并引入元空间,主要是为了解决 PermGen 固定大小、容易导致内存溢出、GC 效率低的问题。元空间使用本地内存,具备更灵活的内存分配能力,提升了垃圾收集和内存管理的效率。
PermGen 的局限性
- 固定大小:永久代的内存空间大小在 JVM 启动时是固定的,容易出现 内存溢出(OutOfMemoryError),尤其是在动态加载大量类时。
- 类和方法的存储限制:永久代用于存放类的元数据(类信息、方法等),其容量受限,导致某些应用特别是在大量动态生成类或使用大量第三方库时,容易出现内存管理问题。
- GC 效率低:永久代内大部分存放的类的元数据是都是被使用的,不是垃圾对象,因此无法被回收,回收的效率很低。
Metaspace 的改进
- 使用本地内存:元空间使用的是 本地内存(Native Memory),而不是 JVM 的堆内存,这样使得内存的分配更加灵活,避免了 PermGen 固定大小带来的局限性。
- 自动调整大小:元空间可以根据应用的需要自动扩展大小,从而降低了出现 OutOfMemoryError 的风险,提升了内存使用的灵活性和效率。
- 性能提升:元空间(在堆外)减少了 GC 对类元数据的影响,避免了频繁回收 PermGen 时的停顿,改善了 JVM 的整体性能。
如何监控和调整元空间的大小
JVM 提供了 -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize 的参数来控制元空间的初始和最大大小。如果不设置,元空间会根据需要动态扩展,通常情况下不需要手动调整,但对于特定的大型应用,建议进行调优以避免内存问题。
官方对移除的动机解释
官方写了:因为 JRockit 没有永久代,而 JRockit 要和 Hotspot 融合,所以把 Hotspot 永久代给去了。
其实永久代之前的存在就有点尴尬,归堆管但是实际上回收效率很低,你听听这名字永久代,不就是对象几乎是永久存在吗?
而且有永久代的话,如果永久代满了也会触发 full gc,触发了回收但是回收率又很低,所以很不划算。
因此官方借着和 JRockit 合并就把永久代也干掉,用元空间代替。元空间放在堆外,至少没堆内内存的限制了。
你了解 Java 的类加载器吗?
Java 的类加载器(ClassLoader)是 JVM 中用于动态加载类文件的组件。它将 .class 文件中的字节码加载到内存中,并将其转换为 Class 对象,以供 JVM 执行。
类加载器的作用:
- 动态加载类:在运行时根据需要加载类,而不是在编译时加载所有类。
- 隔离不同的类命名空间:通过不同的类加载器,可以隔离同名类,使得它们不会相互冲突。
类加载器的层次结构
JDK8 的时候一共有三种类加载器:
1)启动类加载器(Bootstrap ClassLoader),它是属于虚拟机自身的一部分,用 C++ 实现的(JDK9 后用 java 实现),主要负责加载
2)扩展类加载器(Extension ClassLoader),它是 Java 实现的,独立于虚拟机,主要负责加载
3)应用程序类加载器(Application ClassLoader),它是 Java 实现的,独立于虚拟机。主要负责加载用户类路径(classPath)上的类库,如果我们没有实现自定义的类加载器那这个加载器就是我们程序中的默认加载器。
在 JDK9 之后,类加载器进行了一些修改,主要是因为 JDK9 引入了模块化,即 Jigsaw,原来的 rt.jar、tool.jar 等都被拆成了数十个 jmod 文件,已满足可扩展需求,无需保留
且双亲委派的路径也做了一定的变化:
在平台和应用类加载器受到加载请求时,会先判断该类是否属于一个系统模块,如果属于则委派给对应的模块类加载器加载,反之才委派给父类加载器。
JDK9 之后类加载器负责模块(图来自网络):

























