前面分享了 JVM调优原则和原理分析 ,详情可查看:这里
基于该文章,我们大体上对 JVM 有一个概况性的理解,同时我们知道了 JVM调优原则 (JVM优化是不得已的手段)
对 调优基础知识 (head堆,垃圾回收器)等知识点,有一个初步的认识。
那今天我们继续分享一下: 垃圾回收算法 ,和 调优的关键指标 ,等内容。
在内存中 没有被引用的对象 就是垃圾(一次请求会在内存中创建出很多的对象,这些对象不会自己消失,必须进行 垃圾回收 ,当然 垃圾回收器 是jvm自己提供的。)
(特别注意:高并发的场景下,内存中尤其会创建海量的对象,这些对象所占用的内存必须及时被释放,否则影响程序性能)
一个对象引用消失了,那么这个对象就变成 垃圾 ;因此这个对象必须被 垃圾回收器 回收;
那说到这里,我们就会有疑问了,怎样判断一个对象,将会成为垃圾呢?
别急,且听哥们一一道来!!!接着往下。
Jvm中有 2 种寻找垃圾对象的方案:
1 、引用计数算法
2 、根可达算法 ----- hotspot垃圾回收器 都是使用这个算法
1) 引用计数算法:
通过引用计数方法,找到这个垃圾:
当这个对象引用都消失了,消失一个计数减一,当引用都消失了,计数就会变为0.此时这个对象就会变成垃圾。
这样,会存在问题:
在堆内存中主要的引用关系有如下三种:
由此可见,引用计数算法不能解决循环引用问题。为了解决这个问题,Java使用了根可达分析算法。
循环引用,使用这个算法,是无法解决的。
那就引出了另外一种算法: 根可达算法
2) 根可达算法:
hotspot目前使用的主要的垃圾回收器的算法,因为引用计数无法解决循环引用计数的问题。
根可达分析算法的基本思想是: 通过一系列名为"GC roots"的对象作为起始点,从这个被称为GC roots的对象开始,向下搜索,如果一个对象到GC roots没有任何引用链相连时,说明此对象不可用。
也即给定一个集合的引用作为 根 出发,通过引用关系遍历对象图,能够遍历到的(可到达)对象就被判定为 存活 ,没有则自然被判定为 死亡 。
所谓的"GC roots"对象起始点,或者说tracing GC的"根集合"就是一组必须活跃的引用。
通过 根可达算法 ,我们已经找出垃圾对象了,接下来,就是清除垃圾。
如何清除垃圾呢?这里JVM提供3种算法。且听哥们一一道来!!!
JVM提供 3 种方法,清除垃圾对象
Mark-Sweep 标记清除算法
Copying 拷贝算法
为了解决效率问题,Copying 拷贝算法将可用内存按容量划分为大小相等的两块,每次只使用其中一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。只是这种算法的代价是将内存缩小为了原来的一半。
Mark-Compact 标记压缩算法
标记压缩算法,把存活对象拷贝到可回收的空间,然后把存活对象空间进行压缩,串联起来,把未使用的对象空间串联起来,组成连续的内存空间,这样就不会有碎片。
有 7 种不同的垃圾回收器,它们分别用于不同分代的垃圾回收。
两个垃圾回收器之间有连线表示它们可以搭配使用,可选的搭配方案如下:
新生代 | 老年代 |
---|---|
Serial | Serial Old |
Serial | CMS |
ParNew | Serial Old |
ParNew | CMS |
Parallel Scavenge | Serial Old |
Parallel Scavenge | Parallel Old |
G1 | G1 |
调优的最终目的都是为了应用程序使用 最小的硬件 消耗来承载更大的 吞吐量
重要指标之一,吞吐量是衡量系统在单位时间里面完成的工作数量。吞吐量需求通常忽略延迟或者响应时间。通常情况下,提升吞吐量需要以系统响应变慢和更多内存消耗作为代价。
延迟或者响应时间是衡量应用从接收到一个任务到完成这个任务消耗的时间。一个延迟或者响应时间的需求需要忽略吞吐量。通常来讲,提升应用的响应时间需要以更低吞吐量或提高应用的内存消耗。
延迟或者响应时间例子:"例如系统处理一个HTTP请求需要200ms,这个200ms就是系统的响应时间。"
内存占用是衡量应用消耗的内存,这个内存占用是指应用在运行在某一个吞吐量、延迟以及可用性和易管理性指标下的内存消耗,内存占用是通常描述为应用运行的时候Java堆的大小或者总共需要消耗内存。
通常情况下,通过增加Java堆的大小以增加应用内存占用可以提升吞吐量或者减少延迟,或者两者兼具。当应用可用的内存减少的时候,吞吐量和延迟通常会受到损失。在给定内存的情况下,应用占用的内存可以限制应用的实例数(这个会影响可用性)。
内存占用需求例子:“这个应用会单独运行在一个8G的系统上面或者多出 3 个应用实例运行在一个24G的应用系统上面。”
好了,以上就是我个人的经验的分享了。