1.JVM堆的基本结构
1. JVM堆空间分为:Java堆和方法区(元空间/永久代)
2. Java堆从GC的角度可以细分为:新生代和老年代
(因此JVM堆可以分为三大区:新生代、老年代、永久代)
3. 新生代又可以细分为:Eden区、两个幸存区(Survivor-From区、Survivor-To区)
* Eden区:new出来的对象都会存储到Eden区(较大对象会直接存储到老年区)
* Survivor-From区:上一次GC的幸存者,第一次出现对象是从Eden区分配过来的
* Survivor-To区:将GC的幸存者复制到该区后,该区将会变为下一次GC的FROM区,原来的From区将会变成To区。to区和from区总是相互复制、相互转换的
- 堆内存大小占比:Eden区:Survivor-FROM区:Survivor-To区<=>8:1:1(可以通过
-XX:SurvivorRatio
修改Eden区内存占比,默认值是8) - 新生代占Java堆内存的1/3(可以通过参数-XX:NewRatio修改,默认值是2,如果
-XX:NewRatio
=3,则新生代和老年代的占比为1:3)
4. 老年代
* 当新生代每进行一次GC,年龄就会+1,直到年龄>=15就会被分配到老年代(-XX:MaxTenuringThreshold)默认值=15
* 老年代占Java堆内存的2/3
* 较大对象会直接存储到老年区
* 存活的对象太多,无法保存在Survivor时会直接进入老年区
注意:new的对象字节大小超过-XX:PretenureSizeThreshold
设置的值,则会直接进入老年区
5. 永久代
* 永久代为`JDK1.8`以前的说法,之后称为`元空间`
* 元空间是方法区(永久代/元空间))的具体实现
* 永久代内存不足会触发虚拟机的full GC,而元空间是采用本地内存进行存储,因此不会触发虚拟机的Full GC
2.GC垃圾回收机制
什么是GC?
GC是垃圾收集的意思,如果不进行垃圾回收,容易造成OOM
可以手动进行垃圾回收吗?
不可以!但是可以通过System.gc()通知GC进行垃圾回收(GC不一定执行)
GC是如何判断对象是否是垃圾的?
两种方式:
可达性分析算法:采用有向图的方式记录和管理堆中的所有对象,从有向图的根节点(GC Root)开始搜索,当有向图的路径(引用链)为不可达,则GC会标记该对象为不可达对象,并在第二次标记时,如果引用链还未关联,则视为可回收对象,并进行回收
引用计数器算法:为每个对象创建一个引用计数,有对象引用时计数器+1,引用被释放时计数-1,当计数器的值为0时GC就会将该对象进行回收(缺点:不能解决循环引用的问题 被主流JVM抛弃)
2.1GC回收器分类
- 新生代收集器(复制算法)
- 常用的收集器:Serial、PraNew、Parallel Scavenge
- Serial收集器(单线程收集器):标记和清理都是单线程,优点是简单高效
- PraNew收集器(并行收集器):Serial的多线程版本,在多核CPU环境下比Serial更高效
- Parallel Scavenge收集器(并行收集器):追求高吞吐量,高效利用CPU。
吞吐量=用户线程时间/(用户线程时间+GC线程时间)
,可尽快完成程序的运算任务,适合后台应用等,对交互响应要求不高的场景。
- 常用的收集器:Serial、PraNew、Parallel Scavenge
- 老年代收集器
- 常用的收集器:Serial Old、CMS、Parallel Old
- Serial Old收集器(标记-整理算法):老年代单线程收集器
- CMS(标记-清除算法):老年代并行收集器,以获取最短回收停顿时间为目标的收集器,具有高并发、低停顿的特点,追求最短GC回收停顿时间。
- Parallel Old(标记-整理算法):老年代并行收集器,吞吐量优先
- 常用的收集器:Serial Old、CMS、Parallel Old
- G1收集器(标记-整理算法)
- Java堆并行收集器,G1收集器是JDK1.7提供的一个新的收集器,G1收集器基于"标记-整理"算法实现,也就是说解决了内存碎片的产生问题,且G1的回收范围是整个Java堆(包括新生代、老年代)
2.2GC收集器使用的过程
-
新生代收集器称为:MinorGC
- 当Eden区满了以后,此时会采用可达性分析算法对Eden区的数据进行标记清除,剩余存活对象将被复制到幸存区(Survivor-From),清空Eden区域对象,如图所示:
- 当Form幸存区满了以后触发第二次MinorGC,此时将会利用可达性分析算法判断Eden区和Form区标记的可回收对象并清理,利用复制算法将Eden区和Form区存活的对象复制到To区,并清空Eden区和Form区的对象
- Survivor-From区更名为Survivor-To区,而原Survivor-To区替换为Survivor-From区
- 下一次GC同样会把Eden区和现在的Form区存活对象复制到To区,如此往复,直到对象年龄>=15则会被分配到老年区
- 当Eden区满了以后,此时会采用可达性分析算法对Eden区的数据进行标记清除,剩余存活对象将被复制到幸存区(Survivor-From),清空Eden区域对象,如图所示:
-
老年代收集器称为:MajorGC
- 当新生代对象分配到老年代,导致内存空间不足,触发MajorGC;或new的对象过大,直接分配到老年代时无法找到连续内存空间分配给该对象时,也会提前触发MajorGC
首先会扫描一次所有的老年代对象,标记存活对象,该过程称为标记阶段
标记后会将可回收对象清理,该过程称为清理阶段