原理:
GC是垃圾收集的意思(Garbage Collection),Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的。
简而言之,GC是将java的无用的堆对象进行清理,释放内存,以免发生内存泄露。
**问题一:**为什么需要垃圾回收?
**答:**如果不进行垃圾回收,内存迟早都会被消耗空,因为我们在不断的分配内存空间而不进行回收。除非内存无限大,我们可以任性的分配而不回收,但是事实并非如此。所以,垃圾回收是必须的。
**问题二:**常见的垃圾回收算法有哪些?
答:
1、标记-清除算法 (老年代GC采用的算法)
分为两个阶段:标记阶段和清除阶段
标记阶段:首先标记出所有需要回收的对象。
清除阶段:统一回收所有被标记的对象。
缺点:标记和清除过程效率都不高,会产生大量不连续的内存碎片,导致无法给大对象分配内存。
这种算法的不足主要体现在效率和空间,从效率的角度讲,标记和清除两个过程的效率都不高;从空间的角度讲,标记清除后会产生大量不连续的内存碎片, 内存碎片太多可能会导致以后程序运行过程中在需要分配较大对象时,无法找到足够的连续内存而不得不提前触发一次垃圾收集动作。
2、复制算法 (新生代GC****采用的算法)
分为两个阶段:标记阶段和复制阶段
标记阶段:首先需要先标记出存活的对象。
复制阶段:把存活的对象都复制到一块新的空内存里去,最后将原来的内存空间清空。
复制算法是为了解决效率问题而出现的,它将可用的内存分为两块,每次只用其中一块,当这一块内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已经使用过的内存空间一次性清理掉。
缺点:内存缩小为了原来的一半,在对象存活率较高的场景下要进行大量的复制操作,效率很低。
3、标记-整理算法
分为三个阶段:标记阶段和整理阶段和清除阶段
标记阶段:首先需要先标记出存活的对象。
整理阶段:将所有的存活对象压缩到内存的一端。
清除阶段:把存活边界外的内存空间都清除一遍。
4、分代收集算法:
存活率低:少量对象存活,适合复制算法:在新生代中,每次GC时都发现有大批对象死去,只有少量存活(新生代中98%的对象都是“朝生夕死”),那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成GC。
存活率高:大量对象存活,适合用标记-清理和标记-整理算法:在老年代中,因为对象存活率高、没有额外空间对他进行分配担保,就必须使用“标记-清理”/“标记-整理”算法进行GC。
现代商用虚拟机基本都采用分代收集算法来进行垃圾回收。这种算法没什么特别的,无非是上面内容的结合罢了,根据对象的生命周期的不同将内存划分为几块,然后根据各块的特点采用最适当的收集算法。大批对象死去、少量对象存活的(新生代),使用复制算法,复制成本低;对象存活率高、没有额外空间进行分配担保的(老年代),采用标记-清理算法或者标记-整理算法。
问题三:介绍一下新生代、老年代、永久代和各种GC
答:
JVM 中的堆,一般分为三大部分:新生代、老年代、永久代
1.新生代
主要是用来存放新生的对象,新生代通常存活时间较短。一般占据堆的 1/3 空间。由于频繁创建对象,所以新生代会频繁触发 MinorGC(也叫新生代GC) 进行垃圾回收。
MinorGC:采用复制算法。(问题二有介绍复制算法)
新生代分为 Eden 区、ServivorFrom、ServivorTo 三个区。
- Eden 区:Java 新对象的出生地(如果新创建的对象占用内存很大,则直接分配到老年代)。当 Eden 区内存不够的时候就会触发 MinorGC,对新生代区进行一次垃圾回收。
- ServivorTo:保留了一次 MinorGC 过程中的幸存者。
- ServivorFrom:上一次 GC 的幸存者,作为这一次 GC 的被扫描者。
当 JVM 无法为新建对象分配内存空间的时候 (Eden 满了),Minor GC 被触发。因此新生代空间占用率越高,Minor GC 越频繁。
可以看出触发新生代GC的条件是Eden 满了
2. 老年代
老年代的对象比较稳定,对象存活的时间比较长。所以 MajorGC 不会频繁执行。在进行 MajorGC(也叫老年代GC) 前一般都先进行了一次 MinorGC(新生代GC),使得有新生代的对象晋身入老年代,导致空间不够用时才触发。当无法找到足够大的连续空间分配给新创建的较大对象时也会提前触发一次 MajorGC 进行垃圾回收腾出空间。
MajorGC:采用标记—清除算法 (问题二有介绍标记—清除算法) *有的地方等同于Full GC,有的地方单单指老年代的GC。
3.永久代
指内存的永久保存区域,主要存放 Class 和 Meta(元数据)的信息。
Class 在被加载的时候被放入永久区域。它和和存放实例的区域不同,GC 不会在主程序运行期对永久区域进行清理。所以这也导致了永久代的区域会随着加载的 Class 的增多而胀满,最终抛出 OOM 异常。
在 Java8 中,永久代已经被移除,被一个称为 “元数据区”(元空间)的区域所取代。
元空间的本质和永久代类似,都是对 JVM 规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。类的元数据放入 native memory, 字符串池和类的静态变量放入 java 堆中. 这样可以加载多少类的元数据就不再由 MaxPermSize 控制, 而由系统的实际可用空间来控。
问题四:堆是怎么构成的?
答:堆 = 新生代 + 老年代
其中,新生代 ( Young ) 被细分为 Eden 和 两个 Survivor 区域,这两个 Survivor 区域分别被命名为 From Survivor 和 To Survivor ,以示区分。
默认的,Edem : from : to = 8 : 1 : 1 。可以通过参数 –XX:SurvivorRatio 来设定 ,即:
Survivor区和Eden区的比值
问题五:Java中是怎么判断一个对象是垃圾?
答:可达性分析算法
Java中定义了一些起始点,称为GC Root,当有对象引用它的时候,就把对象挂载在它下面,形成一个树状结构,当一个对象处于一个这样的树里时,就认为此对象是可达的,反之是不可达
如图 Object1,Object2,Object3是对象可达的,Object4,Object5,Object6是不可达的。
持续更新中…
参考博客:
Java内存垃圾回收(GC)原理_Zhang.Voi的博客-CSDN博客_java内存回收机制原理
https://www.jianshu.com/p/3d61a0e0e006