前言
最近被问到了这个问题, 解释的不是很清晰, 有一些概念略微模糊, 在此进行整理和记录, 分享给大家. 本篇文章主要讲解内存区域的年轻代, 老年代和永久代, 略微提及一些垃圾回收算法, 下面是正文.
堆整体
堆主要用于存放各种类的实例对象和数组. 在 java 中被分为两个区域: 年轻代和老年代. 在 java 中还有一个永久代的意思, 这里最后会单独说明.
年轻代和老年代的划分是为了更好的内存分派及回收. 提高效率.
堆是垃圾回收机制的重点区域. 我们知道垃圾回收机制有三种, minor gc,major gc 和 full gc. 针对于堆的就是前两种. 年轻代的叫 minor gc, 老年代的叫 major gc.
年轻代
年轻代用来存放新近创建的对象, 尺寸随堆大小的增加和减少而相应的变化, 默认值是保持为堆的 1/15.
年轻代的大小可以通过 - xmn 设置固定大小, 也可以通过 - xx:newratio 设置年轻代和年老代的比例.
年轻代中存在的对象是死亡非常快的. 存在朝生夕死的情况.
所以为了提高年轻代的垃圾回收效率, 又将年轻代划分为三个区域, 一个 eden 和两个 sunrvivor from.
eden 和 survivor 默认比例是 8:1:1, 进行垃圾回收采用的是分代复制算法. 每次新生代的使用, 会是 eden 区和一块 survivor 区. 当我们进行垃圾回收的时候, 清除正在使用的区域, 将其中的存货对象, 放入到另一个 survivor 区域, 并进行整理, 保证空间的连续. 如果对象长时间存活, 则将对象移动到老年区. 存活下来的对象, 他的年龄会增长 1. 当对象的年龄一次次存活, 一次次增长, 到达 15 的时候, 这些对象就会移步到老年代. 在年轻代执行 gc 的时候, 如果老年代的连续空间小于新生代对象的总大小, 就会触发一次 full gc. 是为了给新生代做担保, 保证新生代的老年对象可以顺利的进入到老年代的内存区.
老年代
老年代中存放的对象是存活了很久的, 年龄大于 15 的对象. 在老年代触发的 gc 叫 major gc 也叫 full gc.full gc 会包含年轻代的 gc. 但老年代只要执行 gc 就一定是 full gc.
full gc 采用的是标记 - 清除算法. 会产生内存碎片. 在执行 full gc 的情况下, 会阻塞程序的正常运行. 老年代的 gc 比年轻代的 gc 效率上慢 10 倍以上. 对效率有很大的影响.
永久代
永久代是 hotspot 虚拟机, 也就是我们使用的 java 虚拟机的特有的概念, 他不属于堆内存, 是方法区的一种实现, 各大厂商对方法区有各自的实现. 永久代存放 jvm 运行时, 需要的类, 包含 java 库的类和方法, 在触发 full gc 的情况下, 永久代也会被进行垃圾回收. 永久代的内存溢出也就是 pergen space.
元空间
元空间是 metaspace, 在 jdk1.8 的时候, jvm 移除了永久代的概念, 元空间也是对 java 虚拟机的方法区的一种实现. 元空间与永久代最大的区别在于, 元空间不在虚拟机中, 使用本地内存. 通过配置如下参数可以更改元空间的大小.
-XX:MetaspaceSize: 初始空间的大小. 达到该值就会触发垃圾收集进行类型卸载, 同时 GC 会对该值进行调整: 如果释放了大量的空间, 就适当降低该值; 如果释放了很少的空间, 那么在不超过 MaxMetaspaceSize 时, 适当提高该值.
-XX:MaxMetaspaceSize, 最大空间, 默认是没有限制的.
永久代的回收会随着 full gc 进行移动, 消耗性能. 每种类型的垃圾回收都需要特殊处理元数据. 将元数据剥离出来, 简化了垃圾收集, 提高了效率.
来源: https://www.cnblogs.com/jichi/p/12580906.html