想拿高工资,想成为一名合格又优秀的java高级攻城狮,对于JVM的学习是必不可少的。
我本人找过很多课程,学过很多遍,却总是感觉学不太明白,感觉少点什么,我相信很多小伙伴会和我有一样的经历。
还好现在找到一个比较易于理解却不臃肿的视频教程,本笔记就是基于视频教程以及对视频中不易理解的部分进行多方咨询求证,力求写出一篇易于理解,帮助小伙伴们成长的教程,在这里非常感谢黑马的课程。视频:黑马程序员JVM完整教程
第一篇:JVM 完整教程(1/3):内存结构
第二篇:JVM 完整教程(2/3):垃圾回收
第三篇:JVM 完整教程(3/3):类加载与字节码技术&内存模型
定义
给对象的引用进行计数,每当有一个地方引用该对象时,计数器就加1,当引用失效时,计数器就减1。当计数器为0的时候就代表该对象没有被引用,就会被垃圾回收。
弊端
循环引用,当A对象和B对象互相引用,而且AB对象都没有被其他对象引用时,A和B的计数器永远都是1,永远不会被回收。因此,Java虚拟机并未使用引用计数法。
- Java虚拟机中的垃圾回收器通过可达性分析算法来探索所有存活的对象。
- 扫描堆中的对象,看能否沿着GC Root对象为起点的引用链找到该对象,找不到,表示可以回收。
- 哪些对象可以作为GC Root?
- 虚拟机栈中引用的对象
- 本地方法栈中 JNI(即一般说的Native方法)引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 被同步锁synchronized持有的对象
举个栗子叭
比如我们提起盘子里的一串葡萄,我们手提的部位就是GC Root,掉在盘子里的葡萄就相当于不在引用链上的对象,也就是上述可以被回收的对象。
强引用是使用最普遍的引用,也是最常用的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。如下:Object strongReference = new Object();
当内存空间不足时,Java虚拟机宁愿抛出OutOfMemoryError异常,也不会回收具有强引用的对象。
在垃圾回收时,如果内存空间不足了,就会回收软引用对象,如果内存空间充足就不会回收。
一般会用于缓存,就算被回收也不会对系统造成影响。
在垃圾回收时,无论内存是否充足,都会回收弱引用对象。
与软引用的区别在于:软引用对象只有内存不足才会被回收,而弱引用对象一旦被发现都会被回收。
虚引用顾名思义,就是形同虚设。与其他几种引用都不同,虚引用并不会决定对象的生命周期。仅仅在被垃圾回收时收到一个系统通知,一般用于记录对象的垃圾回收。
1.3.5 总结
Java中4种引用的级别和强度由高到低依次为:强引用 -> 软引用 -> 弱引用 -> 虚引用
标记清楚算法分为两个阶段:标记和清除。
标记:标记出GC Root引用链不可达对象。
清除:将标记的对象进行垃圾回收,释放内存。
原理如下图:蓝色代表强引用对象(即不可回收的对象),灰色代表不可达的对象(即可回收的垃圾对象),白色区域代表空闲内存。由下图可以看出灰色部分先被标记为垃圾对象,然后被清释放内存空间。
由上图可以看出,标记清除算法,能够快速的标记出垃圾对象并清除,但是没有对释放的内存空间进行整理,导致内存空间不连续,产生内存碎片(假如上图释放的三个内存空间分别为3M、2M、1M,这时又有一个新的对象需要5M的内存空间,那么这三个不连续的内存空间都放不下该对象,实际上被释放的总内存为6M是大于5M的,所以这三个内存空间就变成了不可用的内存碎片)。
优点:
- 速递快
缺点:
- 会产生内存碎片
标记整理算法也分为两个阶段:标记和整理。与标记清除算法的区别就是,它将释放的内存空间进行里整理,所以被释放的内存空间都是连续的,不会产生内存碎片,当然整理内存也是有点耗时的,所以速度没有标记清除算法快。如下图所示:
优点:
- 不会产生内存碎片
缺点:
- 速度比较慢
复制算法是先将内存空间分为两部分,这里就用A(系统使用的内存空间)和B(空闲的内存空间)表示两个内存空间。首先标记出A空间的垃圾对象,将标未标记的使用中的对象放到B空间,然后清除A空间的垃圾对象,最后把A空间和B空间调换一下位置(A空间变成空闲的内存空间,B空间变成系统使用的内存空间)。如下图:
优点:
- 不会产生内存碎片
缺点:
- 占用双倍内存空间
先看一张清晰明了的图,了解一下分代垃圾回收是怎么回事。
根据上图来个小结:
- 对象首先分配在伊甸园区。
- 伊甸园空间不足时,触发Minor GC,伊甸园和From存活的对象会复制到To中,存货的对象年龄+1并且交互From和To。
- Minor GC 会引发 stop the world,暂停其他线程,等垃圾回收结束后,恢复用户线程运行。
- 当幸存区对象的寿命超过阈值时,会晋升到老年代,最大的寿命是15次,如果内存紧张,不达到阈值也会晋升到老年代。
- 当老年代空间不足时,会先触发Minor GC,如果空间仍然不足,那么就触发 Full GC ,Full GC也会引发stop the world,只不过停止的时间更长,如果空间还是不足就会报OOM异常。
| 含义 | 参数 |
|---|---|
| 对初始大小 | -Xms |
| 对最大大小 | -Xmx 或 -XX:MaxHeapSize=size |
| 新生代大小 | -Xmn 或 -XX:NewSize=size + -XX:MaxNewSize=size |
| 幸存区比例(动态) | -XX:InitialSurvivorRatio=ratio 和 -XX:+UseAdaptiveSizePolicy |
| 幸存区比例 | -XX:SurvivorRatio=ratio (默认ratio是8) |
| 晋升阈值(晋升到老年代的阈值) | -XX:MaxTenuringThreshold=threshold |
| 晋升详情 | -XX:+PrintTenuringDistribution |
| GC详情 | -XX:+PrintGCDetails -verbose:gc |
| FullGC 前 MinorGC (默认是打开的) | -XX:+ScavengeBeforeFullGC |
public class Code_10_GCTest {
private static final int _512KB = 512 * 1024;
private static final int _1MB = 1024 * 1024;
private static final int _6MB = 6 * 1024 * 1024;
private static final int _7MB = 7 * 1024 * 1024;
private static final int _8MB = 8 * 1024 * 1024;
// -Xms20m -Xmx20m -Xmn10m -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc
public static void main(String[] args) {
List list = new ArrayList<>();
list.add(new byte[_6MB]);
list.add(new byte[_512KB]);
list.add(new byte[_6MB]);
list.add(new byte[_512KB]);
list.add(new byte[_6MB]);
}
}
首先配置JVM 参数, 然后main函数里先不加代码运行看结果,再然后一行行添加代码分析打印的结果。
4. 垃圾回收器 4.1 串行 5. 垃圾回收调优


