1、jvm内存模型、分区
- 程序计数器:记录当前线程执行的位置
- 方法区:用于储存已被虚拟机加载的类信息,常量池(常量,静态变量),即时编译器编译后的代码
- 堆:存放数组和对象实例
- 栈:定义基本数据类型的变量,一个对象的引用保存在栈里面
Book b = new Book();
new Book()实例是放到堆里面的
b引用是放到栈里面的
2、堆分区
- Java 堆从 GC 的角度细分为: 新生代(Eden 区、From Survivor 区和 To Survivor 区)和老年代。
- 新生代:用来存放新生的对象,一般占据堆的1/3空间,由于频繁创建对象,所以新生代会触发gc进行垃圾回收,新生代分为Eden,servivorTo,servivorFrom区
- Eden区:java新对象的出生地(新创建的对象占用内存很大,直接分配到老年代,当Eden区内存不够时就会触发gc,新生代进行一次垃圾gc)
- servivorTo:上次gc的幸存者,作为这次gc的被扫描者
- servivorFrom:保留一次gc过程的幸存者
新生代(MinorGC 采用复制算法)(复制->清空->互换)(存活对象少、垃圾多)
1.eden、servicorFrom 复制到 ServicorTo,年龄+1
首先,把 Eden 和 ServivorFrom 区域中存活的对象复制到 ServicorTo 区域
(如果有对象的年龄以及达到了老年的标准,则赋值到老年代区),
同时把这些对象的年龄+1(如果 ServicorTo 不够位置了就放到老年区);
2.清空 eden、servicorFrom
然后,清空 Eden 和 ServicorFrom 中的对象;
3.ServicorTo 和 ServicorFrom 互换
最后,ServicorTo 和 ServicorFrom 互换,原 ServicorTo 成为下一次 GC 时的 ServicorFrom区
老年代(MajorGC 采用标记清除算法)(标记整理)(存活对象多、垃圾少)
主要存放应用程序中生命周期长的内存对象
MajorGC 采用标记清除算法:
首先扫描一次所有老年代,标记出存活的对象,然后回收没有标记的对象。
MajorGC 的耗时比较长,因为要扫描再回收。
MajorGC 会产生内存碎片,为了减少内存损耗
我们一般需要进行合并或者标记出来方便下次直接分配(标记整理)。
永久代
指内存的永久保存区域,主要存放 Class 和 Meta(元数据)的信息,Class 在被加载的时候被放入永久区域
它和和存放实例的区域不同,GC 不会在主程序运行期对永久区域进行清
所以这也导致了永久代的区域会随着加载的 Class 的增多而胀满,最终抛出 OOM 异常
在 Java8 中,永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代
元空间的本质和永久代类似,元空间与永久代之间最大的区别在
GC回收机制的方法
- 标记-清理:通过可达性遍历堆内存,把'存活'对象与'垃圾'对象进行标记,把所有垃圾对象所占的空间清空,但容易产生内存碎片
- 标记-整理:在清理的时候,把所有的存活对象扎堆到同一个地方,让他们待在一起,就没有内存碎片了
- 复制:把堆内存分成两部分,一段时间内只允许在一块内存区进行分配,分配完成后,则执行垃圾回收