stop the world
首先:
要了解一个概念:
Object obj = new Object();
64位操作系统中初始化占用16子节,至于为什么是16字节,就包括对象头,对象实际数据 对齐填充三部分组成
那new一个对象需要占用多少内存?
@Data
public class TestPojo {
private Long seqId;
private String bedCode;
private Date callTime;
private int status;
}
//打印下占用多少内存
public static void main(String[] args) {
ClassLayout layout = ClassLayout.parseInstance(new TestPojo());
System.out.println(layout.toPrintable());
}
结果:
如图:
4个参数初始化内存都是4,那么 头部占用8子节,内容占16,总字节数24 bytes
那么jvm分配的内存块是大小不一致的,回收起来的内存块也是大小不一,没有办
垃圾回收算法也有三种,怎么选择自己的垃圾回收算法呢?
-
标记清除法 Mark-sweep
-
复制算法 Coping
-
标记整理算法
-
分代收集算法
标记清除算法,就是把可回收的对象标记成可回收,清除阶段直接清除这部分内存,这样在内存里面就会有很多的内存碎片,导致GC的频繁触发复制算法,将内存分成2块,每次只使用其中的一块,当一块使用完成后,直接将这一块里面还存活的对象拷贝到另外一块上去,将这一块直接一次清理掉,不会有内存碎片,但是内存只能使用一半,copy的效率等等,会导致内存的浪费,拷贝的效率第等问题
标记整理算法,标记对象的存活,将存活的对象往内存的一端移动,记录这个边界,清理掉边界以外的内存。这样就会节省内存空间。
分代收集就是 jvm现在使用的算法,将将内存划分成,老年代2/3和新生代1/3,老年代每次回收少量对象,新生代每次回收大量对象
新生代使用 复制算法,每次回收的多,每次将Eden区(8/10)和Surverior区(1/0)的存活对象拷贝到serverior区(两个共1/10) 里面 ,直接当前清理Eden区和Serverior区,也就是说每次只剩余一个serverior区里面内存占有数据, 这就是为什么新生代里面有一个Eden区和两个serverior区;
老年代使用 标记压缩算法,每次清理掉可回收对象
确定了算法,我们开始确定算法回收的时候使用那种回收器,回收器决定了,GC什么时候触发回收,回收是单线程还是多线程,回收的时候是否stop the word
垃圾回收器
jdk8默认使用的 Parallel回收器
- Serial单线程垃圾回收器 – 单个线程单独回收,也就是说,只创建一个线程,暂停其他线程操,专门回收垃圾
- ParNew 垃圾回收器(新生代) – 回收垃圾的时候,暂停其他线程,创建多个线程一起回收垃圾
- Parallel Scavenge 回收器(新生代) – 吞吐量优先收集器,使用复制算法, 并行多线程的垃圾回收器 — java8之后默认的新生代垃圾回收器
- Parallel Old垃圾回收器(老年代) – 多线程,采用标记-压缩算法 (java8之后老年代垃圾回收器)
- CMS 回收器(老年代) – 无法解决跟用户线程并发导致的浮动垃圾,使用标记清除算法有空间碎片问题, 因为是用户线程并发,无法使用拷贝算法和标记压缩算法,内存利用率低~~
为了将每次stop the world 打散,也就是说GC的时候也可以进行用户进行其他操作,
提高服务器的响应效率- 初始标记 STW 标记跟对象 (停止用户线程)
- 并发标记 根据GC root找到存活对象 (跟用户线程并发)
- 重新标记 修正用户线程运行期间的变动对象 (停止用户线程)
- 并发清除 对标记对象清理 (跟用户线程并发)
- G1回收器(整个内存部分新生代老年代了,直接按照内存块回收) --还不成熟