本文是个人读后感,下面是一些零散的理解,并不系统和细腻!
对象何时回收
- 引用计数算法
给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1 ;当引用失效时,计数器值就减1 ;任何时刻计数器都为0的对象就是不可能再被使用的。
PS:Java语言中没有选用引用计数算法来管理内存,其中最主要的原因是它很难解决对象之间的相互循环引用的问题。 - 根搜索算法
通过一系列的名为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。
在根搜索算法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历两次标记过程。
回收方法区
永久代的垃圾收集主要回收两部分内容:废弃常量和无用的类。
- 常量
没有任何对象引用常量池中的对应常量,也没有其他地方引用了这个字面量。这时发生内存回收的话,会被处理。 - 类
1.该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例。
2.加载该类的ClassLoader已经被回收。
3.该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
当满足以上三个条件时,此类可以进行回收,但不必然。
垃圾回收算法
- 垃圾回收算法
首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。
- 复制算法
它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
现在的商业虚拟机都采用这种收集算法来回收新生代。
- 标记-整理算法
根据老年代的特点,有人提出了另外一种“标记-整理”(Mark-Compact)算法,标记过程仍然与“标记一清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
- 分代收集算法
根据对象的存活周期的不同将内存划分为几块。据各个年代的特点采用最适当的收集算法。
垃圾收集器
如果说收集算法是内存回收的方法论,垃圾收集器就是内存回收的具体实现。两个收集器有可能可以搭配使用。
内存分配与回收策略
Java技术体系中所提倡的自动内存管理最终可以归结为自动化地解决了两个问题:给对象分配内存以及回收分配给对象的内存。
- 回收策略
上面已经讲述 - 内存分配
大多数情况下,对象在新生代Eden区中分配。当Eden区没有足够的空间进行分配时,虚拟机将发起一次Minor GC(即对新生代GC)。 - 大对象直接进入老年代
所谓大对象就是指,需要大量连续内存空间的java对象。经常出现大对象容易导致内存还有不少空间时就提前触发垃圾收集以获取足够的连续空间来“安置”它们。虚拟机提供了一个-XX:PretenureSizeThreshold参数,令大于这个设置值的对象直接在老年代中分配。这样做的目的是避免在Eden区及两个Survivor区之间发生大量的内存拷贝。 - 长期存活的对象将进入老年代
虚拟机给每个对象定义了一个对象年龄(Age)计数器。如果对象在Eden出生并经过第一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并将对象年龄设为1。对象在Survivor区中每熬过一次 Minor GC,年龄就增加1岁,当它的年龄增加到一定程度(默认为15岁)时,就会被晋升到老年代中。对象晋升老年代的年龄阈值,可以通过参数-XX:Max TenuringThreshold来设置。 - 动态对象年龄判定
如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。