JVM底层原理分析:2.堆内存分析和JVM调优

【专栏目录】
JVM底层原理分析:1.JDK体系、JVM架构和运行时数据区
JVM底层原理分析:2.堆内存分析和JVM调优

1.堆内存内部结构

组件 说明
年轻代(新生代) 新创建的对象会先存放在年轻代,一般占堆内存的1/3,包括:伊甸区、幸存区From、幸存区To,占比分别为年轻代的8/10、1/10、1/10
老年代 存放由年轻代转过来的对象,一般占堆内存的2/3

【如图】

JVM底层原理分析:2.堆内存分析和JVM调优

2.年轻代内部结构

2.1结构说明

组件 说明
伊甸区 1.新创建的对象一般先放在年轻代中的伊甸区,当伊甸区填满后,会触发minor GC(普通垃圾回收)机制,垃圾回收遵循“GC Roots(可达性分析算法)”原则;2.GC会回收年轻代(包括幸存区在内)中所有不用的对象,而伊甸区内的对象没有被回收的,就是幸存的,会被放入幸存区From或者To中,只能放其中的一个.比如放到了幸存区From中,然后给这些幸存的对象计数,然后把右边的幸存区To里面的幸存对象复制到幸存区From,幸存次数+1.然后右边幸存区To里面的内容全部删除掉。;3.当幸存对象的年纪到了一定年纪,就放到老年代中。
幸存区From 1.新生代中,eden和survivor占比是8:1。也就是新生代内存中,eden占用80%。suivior各占比10%;
2.幸存区From和To永远都是至少有一个为空,因为每次发生minor GC时,都会将当前存在对象的幸存区中再次幸存的对象复制到另一个幸存区,然后再清空当前幸存区;
3.同时也说明,新生代永远有10%的空余。这样设计的原因也是考虑到新生代里面的对象一般都是朝生夕死,大概占了80%。
幸存区To

2.2可达性分析算法:GC Roots

简单理解什么是GC Roots:GC?垃圾回收?GCRoots?简单聊,想深入学习GC Roots底层实现,可自行搜索学习。

3.老年代详解

3.1老年代中的对象来源

每次伊甸区被填满,触发一次minor GC(普通垃圾回收),幸存区中如果有对象,那么幸存区中的对象也会被minor GC,因为minor GC是专门回收年轻代(包括幸存区)中的对象。如果幸存区每经历一次minor GC后仍然能幸存的对象,就会年龄+1(这个值保存在Java对象的对象头中,对应字段叫“分段年龄”),当达到15岁,即幸存15次(默认值)之后,就会转移到老年代。除此之外,如果从伊甸区转移到幸存区的对象,大小直接超过了当前幸存区的50%,那么该对象就会直接被转移到老年代,这是JVM的“对象动态年龄判断”机制规定的。

3.2老年代的垃圾回收

当老年代也被填满的时候,就会触发Full DC。Full GC 是回收老年代+新生代垃圾的。但是Full GC会出现所谓的STW(stop the world)现象。既所有的进程都是挂起等待清理垃圾。执行FGC时,整个程序就是卡顿状态。

3.3STW

概念:Stop-The-World机制简称STW,在执行垃圾收集算法时,Java应用程序的其他所有线程都被挂起(除了垃圾收集帮助器之外)。Java中一种全局暂停现象,全局停顿,所有Java代码停止,native代码可以执行,但不能与JVM交互;这些现象多半是由于gc引起。

Q:为什么要有STW?

A:垃圾回收首先是要经过标记的。对象被标记后就会根据不同的区域采用不同的收集方法。当虚拟机完成两次标记后,便确认了可以回收的对象。但是,垃圾回收并不会阻塞我们程序的线程,它是与当前程序并发执行的。所以问题就出在这里,已经被标记一次的对象有可能会被重新调用使用,如果这个时候该对象没重写finalize()方法,就会被回收,导致程序出错。简单来说就是,如果GC时不暂停程序的线程就可能会回收不该回收的对象。 同样,如果某个对象正在被线程使用,没有被标记,当GC时恰好该对象将不再被使用了,但是由于没有被标记两次就会被忽略回收,即如果没有STW,也可能会遗漏该回收的对象。

4.JVM调优

4.1JVM调优的目的

减少STW的时间!!!

4.2方法

参考我的另一篇博客:Tomcat底层原理分析:9.Tomcat性能调优