Java虚拟机学习(二)-内存管理

我们都知道Java有一个鲜明的特点,就是内存的动态分配和自动回收,这使得开发者不必自己管理内存的分配与回收,可以将主要精力放在程序开发中。同时,内存管理机制也使得Java变得容易上手。
我们虽然在开发的过程中不用关心内存的回收,但是也难免会遇到内存溢出、内存泄漏这些问题。所以学习Java的内存管理有利于我们及时的发现问题。接下来就是我学习Java内存管理机制的一些笔记。
目录:
1.Java对象的判“活”
2.垃圾收集算法
3.HotSpot虚拟机中垃圾收集的实现
4.Minor GC和Full GC

1.Java对象的判“活”
在Java进行垃圾收集前,第一件要做的事情就是判断哪些对象“存活”。
(1)引用计数法:给对象添加一个计数器,当对象被引用是,计数器的值加1;当引用失效时,计数器的值减1;而计数器为0,则表示该对象不再被使用,也就是对象已“死”。
(2)可达性分析:以一个被称为“GC Roots”的对象为起点向下搜索,以搜索的路径为引用链;如果一个对象到“GC Roots”没有引用链,则这个对象是不可达的,那么就会被判定为可回收的对象。

2.垃圾收集算法
(1)标记-清除算法:一个基础的收集算法,之后的一些算法都是在此基础上进行改进的。顾名思义,标记-清除算法就是对对象先标记,在清除。而标记就是建立在Java对象判“活”的基础上。但是标记-清除算法有它的不足之处就是标记和清除的过程不够高效,而且清理过后会产生空间碎片的问题。
这里写图片描述
(2)复制算法:它将内存分为两块,每次使用其中的一块。当这一块内存的空间达到回收的条件时,将存活的对象复制到另一块内存中去,然后将这一块内存中的空间一次性清理掉。
这里写图片描述
在复制算法中可以发现内存是分为两块的,如果两块内存大小设计的不合理就会导致内存的浪费。我们知道在对内存中有新生代,这里是存储对象的地方同时也是垃圾收集的目标内存。在实际使用中新生代有两种内存,一个被称为“Eden”,另外一个是“Survivor”。两者的大小比例是
Eden:Survivor = 8:1,其中Survivor也可以分为From Survivor和To Survivor,也就是新生代是Eden + From Survivor + To Survivor组成的。当复制算法执行时,将Eden和From Survivor空间的存活对象复制到To Survivor空间上,然后将Eden和From Survivor的空间回收,如果To Survivor的空间不够,就会将对象分配到老年代。
(3)标记-整理算法:这种算法通常会在老年代中使用,因为老年代中的对象不容易被回收,这样对象的存活率较高,使用复制算法的效率就会变低。标记-整理算法的过程与标记-清除算法的过程一样,只是在清除完之后会将存活的对象进行整理,移动到内存的一端。
这里写图片描述

3.HotSpot虚拟机中垃圾收集的实现
在HotSpot虚拟机中,垃圾的收集过程也是标记-回收的一个过程。首先,在可达性分析中,需要保持一致性,也就是在可达性分析时,对象的引用关系不可以再发生变化,这是就会发生GC停顿,也就是“Stop the World”。但是如果一个不漏的分析每个对象,那么也需要花费大量的时间,在HotSpot中为了解决这个问题,就使用了一种称为OopMap的数据结构来记录引用的位置。这样GC在扫描是就可以直接找到相应的位置。
但是也不是在每条指令执行后就对OopMap进行更新,这里就有了一个安全点的概念,在程序中会设置安全点,当程序运行到达安全点时,就将程序停顿进行GC。
安全点解决了如何进入GC的问题,但是当程序停顿时又如何解决?这个时候就有了安全域的说法,安全域就是一段代码片段,在安全域中任意地方开始GC都是安全的。

4.Minor GC和Full GC
Java虚拟机中的GC方式有两种:Minor GC和Full GC。
Minor GC发生在Java堆中的新生代,通常采用复制算法,也就是之前所介绍的算法。新生代几乎是所有 Java 对象出生的地方,即 Java 对象申请的内存以及存放都是在这个地方。Java 中的大部分对象通常不需长久存活,具有朝生夕灭的性质。当一个对象被判定为 “死亡” 的时候,GC 就有责任来回收掉这部分对象的内存空间。新生代是 GC 收集垃圾的频繁区域。当对象在 Eden ( 包括一个 Survivor 区域,这里假设是 from 区域 ) 出生后,在经过一次 Minor GC 后,如果对象还存活,并且能够被另外一块 Survivor 区域所容纳( 上面已经假设为 from 区域,这里应为 to 区域,即 to 区域有足够的内存空间来存储 Eden 和 from 区域中存活的对象 ),则使用复制算法将这些仍然还存活的对象复制到另外一块 Survivor 区域 ( 即 to 区域 ) 中,然后清理所使用过的 Eden 以及 Survivor 区域 ( 即 from 区域 ),并且将这些对象的年龄设置为1,以后对象在 Survivor 区每熬过一次 Minor GC,就将对象的年龄 + 1,当对象的年龄达到某个值时 ( 默认是 15 岁,可以通过参数 -XX:MaxTenuringThreshold 来设定 ),这些对象就会成为老年代。
Full GC 是发生在老年代的垃圾收集动作,所采用的是标记-清除算法或者标记-整理算法。老年代里面的对象几乎个个都是在 Survivor 区域中熬过来的,它们是不会那么容易就 “死掉” 了的。因此,Full GC 发生的次数不会有 Minor GC 那么频繁,并且做一次 Full GC 要比进行一次 Minor GC 的时间更长。另外,标记-清除算法收集垃圾的时候会产生许多的内存碎片 ( 即不连续的内存空间 ),此后需要为较大的对象分配内存空间时,若无法找到足够的连续的内存空间,就会提前触发一次 GC 的收集动作。

猜你喜欢

转载自blog.csdn.net/programerxiaoer/article/details/78938292
今日推荐