参考的优秀文章
- 《深入理解Java虚拟机 JVM高级特性与最佳实线》(机械工业出版社)
- Java虚拟机的堆、栈、堆栈如何去理解?
- 聊聊JVM的年轻代
前言
本文是《深入理解Java虚拟机 JVM高级特性与最佳实线》(机械工业出版社)的读书笔记,记录阅读后的部分理解。推荐这本书,深入浅出讲解了Java虚拟机的知识。
疑问及解答
Java运行时有哪几个常用的内存空间?
- PC寄存器
- Java虚拟机栈
- 本地方法栈
- Java堆
- 方法区
PC寄存器
PC为Program Counter的意思,用于记录代码执行的位置。Java是支持多线程的,同时可能有多处代码同时执行,每个线程都需记录各自代码执行的位置,因此,PC寄存器是线程私有的。
用栈来存储什么信息?
Java栈是执行方法时用来存放方法执行所需的信息。一个方法的信息存放为一个栈帧(栈的元素)。一个栈帧包含一个方法所需的信息,包括属性,如8大基本类型、引用类型(栈中只存放引用,实际的对象存放在堆中,后面介绍)和返回地址。
为什么使用栈的数据结构来存储?
因为栈的后进先出特性与方法调用的生命周期特性相吻合。看下图,会让你有所启示。
sequenceDiagram
1方法->>2方法: 调用2方法,入栈(先入栈)
2方法->>3方法: 调用3方法,入栈(后入栈)
3方法->>2方法: 3方法执行完毕,出栈(先出栈)
2方法->>1方法: 2方法执行完毕,出栈(后出栈)
堆空间分布的宏观了解
堆的空间分为:
- 新生代区。主要存放新生代的对象,又可以进一步分为Eden、From Survivor、To Survivor,区如其名,Eden意为“伊甸园”,Survivor意为“幸存者”,他们存放的对象特点相信大家通过其名称能够理解。对象优先在伊甸园分配,对象熬过了一次Minor GC后,可移到幸存区,对象的年龄(age)会加1。
- 老年代区。一般情况下存放长期存活的对象。
- 永久代区。用来存放类信息,想想方法区。
有哪些内存回收算法,及其特点
- 标记-清除算法。标记垃圾对象,然后统一清除。这个算法一个明显的缺点是会造成内存空间不连续。一般用在老年代区。
- 标记-整理算法。标记垃圾对象,然后整理内存空间,整理出连续的内存空间。此算法为标记-清除算法的优化。一般用在老年代区。
- 复制算法。将内存中存活对象复制到To survivor区,然后清空该空间,这样就获得了一块连续的内存空间。鉴于此算法的特点,它不适用于存活对象较多的内存空间,所以一般用在新生代区中。
To survivor与From survivor的区别
使用复制算法对新生代区垃圾回收时,首先将Eden中存活对象复制到To survivor(To survivor此时应该是空的),将From survivor超过年龄阀值的对象复制到老年代区,其他未到年龄的存活对象复制到To survivor,然后清空Eden和From survivor空间,这样就得到了连续可用的内存空间了。然后,From survivor和To survivor对换,这样,From survivor装的是幸存对象,To survivor是空的。
附:
- Eden与Survivor的空间比例默认是8:1,此比例可通过“-XX:SurvivorRatio”参数设置。
- 对象优先分配到Eden(Eden是“伊甸园”的意思,而伊甸园是人类诞生的地儿),熬过一次Minor GC后,对象被移动到Survivor,对象的年龄加1,到达一定年龄阀值可晋升老年代。年龄阀值可通过“-XX:MaxTenuringThreshold”参数配置。
- 复制算法一般适用于新生代区的Minor GC,将存活对象复制到To survivor时,如果存活对象较多较大,会出现To survivor空间不足的情况,需要老年代区作担保,所谓担保,就是由老年代区装这些溢出的对象。极端情况下,连老年代区也有可能不够空间,有一个参数“-XX:-HandlePromotionFailure”设置是否允许担保失败。Minor GC前,如果老年代区的空间大于新生代区,则Minor GC是无空间风险的,可以直接进行Minor GC;否则查看参数是否允许担保失败,如果允许,并且老年代区最大可用空间大于历次晋升到老年代区对象的平均大小,则Minor GC;如果不允许,则进行Full GC。