每个使用 Java 的开发者都知道 Java 字节码是在 JRE 中运行。 JVM 则是 JRE 中的核心组成部分,承担分析和执行 Java 字节码的工作,而 Java 程序员通常并不需要深入了解 JVM 运行情况就可以开发出大型应用和类库。尽管如此,如果你对 JVM 有足够了解,就会对 Java 有更好的掌握,并且能解决一些看起来简单但又尚未解决的问题。
首先,我们来看一下 JVM 不得不知道的体系结构,在编译完之后的 class 文件装载到类装载器的运行时数据区,运行时数据区分为五大块,分别是方法区,堆, Java 栈,本地方法栈,程序计数器。Java栈,本地方法栈,程序计数器存储本地方法接口,方法区和堆则存储执行引擎,也是 GC 的作用区域。
JDK1.8之前,内存管理机制采用分代的策略:分为新生代,老年代,永久代(JDK1.7及以前)。
大致分为 Eden 区和 Survivor 区, Survivor 区又分为大小相同的两部分: s0 ( FromSpace ) 和 s1 ( ToSpace )。新建的对象都是从新生代分配内存, Eden 区不足的时候,会把存活的对象转移到 Survivor 区。当新生代进行垃圾回收时会触发 Minor GC 。一般在Eden区分配对象,使用 TLAB( Thread Local Allocation Buffer ) 进行优化,在保存80%~90%生命周期较短的对象,GC频率高,采用效率较高的复制算法。
新生代的三个分区默认比例为 8:1:1 ,JVM中规定 98% 的对象要在新生代被回收,而 s0 及 s1 只是作为暂存区,每次GC的触发之后必须保证其中一个区是空的,随后两个区互换位置,及 s0 -> s1 (from -> to),s1 -> s0 (to -> from),保证绝大部分对象在此被回收。
旧生代用于存放新生代多次回收依然存活的对象,如缓存对象。当旧生代满了的时候就需要对旧生代进行回收,旧生代的垃圾回收称作 Major GC。新建的对象也有可能直接在旧生代中分配,取决于具体GC的实现。GC频率相对较低,标记,清理,压缩算法的各种结合和优化。
对象经历了多次 Major GC 仍然没有被回收即进入永久代,仅 JDK1.7 及之前的版本拥有。
我们首先来看一下哪些是GC的目标,GC中使用的回收检测算法有两种,目前虚拟机基本上都是用可达性算法:
每个对象有一个引用计数器,当对象被引用一次则计数器加1,当对象引用失效一次则计数器减1,对于计数器为0的对象意味着是垃圾对象,可以被GC回收。
从GC Roots作为起点开始搜索,那么整个连通图中的对象便都是活对象,对于GC Roots无法到达的对象便成了垃圾回收的对象,随时可被GC回收。
从根集合开始扫描,对存活的对象进行标记。
扫描整个内存空间,回收未被标记的对象,使用free-list记录可回收区域。
与标记-清除一样
再次扫描,并往一段滑动存活对象
有问题?可以给我留言或私聊 有收获?那就顺手点个赞呗~
当然,也可以到我的公众号下「6曦轩」,输入“学习”,即可领取一份 【Java工程师进阶架构师的视频教程】~
由于我咧,科班出身的程序员,php,Android以及硬件方面都做过,不过最后还是选择专注于做 Java,所以有啥问题可以到公众号提问讨论(技术情感倾诉都可以哈哈哈),看到的话会尽快回复,希望可以跟大家共同学习进步,关于服务端架构,Java 核心知识解析,职业生涯,面试总结等文章会不定期坚持推送输出,欢迎大家关注~~~