GC
针对java堆的回收。
可达性分析算法:从gc root搜索引用链。
Eden区满时触发minor gc,存活的对象进入survivor区(复制算法),年龄加1,当对象年龄大于阈值(默认15)时,进入老年代。可以通过参数设置阈值,虚拟机运行过程中会进行动态调整。当某个年龄大小超过survivor区一半(默认50%)时,取这个年龄和MaxTenuringThreshold中最小的作为新的阈值。
新生代(标记-复制算法)
- 标记存活对象-因为存活对象较少
- 内存大小eden:to:from = 8 : 1 : 1
- minor gc后,Eden区存活的对象被复制到“survivor To”;“survivor From”区中存活的对象,年龄达到阈值就进入年老代,否则被复制到“survivor To”中。survivor From 和survivor To互换。
- survivor From空间不够用,则放不下的部分会提前进入老年代。
老年代(标记-清除-整理算法)
老年代内存不够时进行full gc(标记-清除),多次full gc后进行整理。次数参数CMSFullGCsBeforeCompaction默认为0次。
强引用:gc不回收。
软引用:内存不足时才回收。
弱引用:gc回收。
虚引用:用于回收前,得到系统通知,可以在回收前进行一些处理。
java内存模型
- 程序计数器:控制程序执行。
- Java虚拟机栈:存储局部变量表、操作数栈、动态连接、方法出口等。
- 本地方法栈:存储本地(native)方法信息。
- Java堆:存储对象实例。
- 方法区:存储静态数据(常量、静态变量等)。包含运行时常量池:存储常量。
Java堆回收算法
- 引用计数法(多数虚拟机不用)
- 可达性分析算法
根据“根对象”搜索其“引用链”判断某对象是否可达,对不可达对象进行筛选,若该对象存在finalize()方法,并且finalize()方法没被执行过,则将该对象放置于队列中,由Finalizer线程执行其finalize()方法来释放该对象。如果队列中的对象在执行finalize()释放前被重新引用,就会移出释放队列。JDK9后被移除。
回收对象判断
- 回收不存在引用的字符串常量(字符串常量池在堆中,运行时常量池在方法区)
- 不在被使用的类,仅仅是“被允许回收”
同时满足:
该类所有实例都被回收;
该类的类加载器已被回收;
该类的Class对象不存在被引用,无法进行发射访问。
GC触发条件
Minor GC触发条件:Eden区满时。
Full GC触发条件:
- 调用System.gc时,系统建议执行Full GC,但是不必然执行。
- 老年代空间不足。
- 方法区空间不足。
- 通过Minor GC后进入老年代的平均大小大于老年代的可用内存。
- 由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小。
【补充】 JDK 7 之前,HotSpot 使用永久代来实现方法区的时候,实现是完全符合这种逻辑概念的。 而在 JDK 7 及之后,HotSpot 已经把原本放在永久代的字符串常量池、静态变量等移动到堆中,这个时候类变量则会随着 Class 对象一起存放在 Java 堆中。