1、为什么永久代要被元空间替代
官网的解答:https://openjdk.org/jeps/122
大概意思就是:因为oracle收购了JRocket,JRocket又号称最快的虚拟机,因为JRocket没有永久代,
所以HotSpot虚拟机也不需要。
随着Java8的到来,HotSpot VM中再也见不到永久代了。但是这并不意味着类的元数据信息也消失了。这些数据被移到了一个与堆不相连的本地内存区域,这个区域叫做元空间(Metaspace)。
由于类的元数据分配在本地内存中,元空间的最大可分配空间就是系统可用内存空间。
这项改动是很有必要的,原因有:
-
为永久代设置空间大小是很难确定的。在某些场景下,如果动态加载类过多,容易产生Perm区的OOM。比如某个实际Web工 程中,因为功能点比较多,在运行过程中,要不断动态加载很多类,经常出现致命错误。
而元空间和永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。 因此,默认情况下,元空间的大小仅受本地内存限制。
-
对永久代进行调优是很困难的。
有些人认为方法区(如HotSpot虚拟机中的元空间或者永久代)是没有垃圾收集行为的,其实不然。《Java虚拟机规范》对方法区的约束是非常宽松的,提到过可以不要求虚拟机在方法区中实现垃圾收集。事实上也确实有未实现或未能完整实现方法区类型卸载的收集器存在(如JDK 11时期的ZGC收集器就不支持类卸载)。 一般来说这个区域的回收效果比较难令人满意,尤其是类型的卸载,条件相当苛刻。但是这部分区域的回收有时又确实是必要的。以前Sun公司的Bug列表中,曾出现过的若干个严重的Bug就是由于低版本的HotSpot虚拟机对此区域未完全回收而导致内存泄漏。
方法区的垃圾收集主要回收两部分内容:常量池中废弃的常量和不再使用的类型。
2、StringTable在Jdk1.7时为什么要从方法区调整到堆区
jdk7中将StringTable放到了堆空间中。因为永久代的回收效率很低,在full gc的时候才会触发。而full gc是老年代的空间不足、永久代不足时才会触发。
这就导致StringTable回收效率不高。而我们开发中会有大量的字符串被创建,回收效率低,导致永久代内存不足。放到堆里,能及时回收内存。
3、JVM各个区都会发生那些异常情况
区域 |
发生异常情况 |
程序计数器 |
程序计数器负责存储执行引擎下一步要执行的字节码指令,是JVM内存结构中唯一一个不会发生异常的区域 |
栈 |
Java虚拟机规范运行Java栈的大小固定或者动态不变。 如果栈设置太小可能会报StackOverflowError;如果设置栈大小动态不变,可能尝试扩展时无法申请到足够的内存,会报OutOfMemoryError |
方法区 |
方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出,虚拟机也会报错; jdk1.7及以前方法区是在永久代,报错信息为:java.lang.OutOfMemoryError: PermGen space jdk1.8及以后方法区是在Metaspace,报错信息为:java.lang.OutOfMemoryError: Metaspace |
堆 |
会报错OutOfMemoryError |
本地方法栈 |
和栈一样 |
4、Eden和Survior的比例分配
JVM内存结构的堆区中分为年轻带、老年代,而年轻代又分为Eden和Survior。Eden和Survior默认比例是8:1:1,即年轻代分为一个Eden区,和两个Survior区。经过研究发现,不同对象的生命周期不同,70%-99%的对象都是临时对象。为了优化GC性能,就有了分带思想。
5、堆是分配对象的唯一选择么
在《深入理解Java虚拟机》中关于Java堆内存有这样一段描述:
随着JIT编译期的发展与逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化,所有的对象都分配到堆上也渐渐变得不那么“绝对”了。
在Java虚拟机中,对象是在Java堆中分配内存的,这是一个普遍的常识。但是,有一种特殊情况,那就是如果经过逃逸分析(Escape Analysis)后发现,一个对象并没有逃逸出方法的话,那么就可能被优化成栈上分配.。这样就无需在堆上分配内存,也无须进行垃圾回收了。这也是最常见的堆外存储技术。
6、逃逸分析是什么
如何将堆上的对象分配到栈,需要使用逃逸分析手段。
这是一种可以有效减少Java程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法。
通过逃逸分析,Java Hotspot编译器能够分析出一个新的对象的引用的使用范围从而决定是否要将这个对象分配到堆上。
逃逸分析的基本行为就是分析对象动态作用域:
-
当一个对象在方法中被定义后,对象只在方法内部使用,则认为没有发生逃逸。
-
当一个对象在方法中被定义后,它被外部方法所引用,则认为发生逃逸。例如作为调用参数传递到其他地方中。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/144619.html