JVM中,程序计数器
、虚拟机栈
、本地方法栈
都是都是线程私有
的,随线程而生随线程而灭,栈帧(栈中的对象)随着方法的进入和退出做入栈和出栈操作,实现了自动的内存清理。
因此,我们的内存垃圾回收主要集中于 java 堆和方法区中,在程序运行期间,这部分内存的分配和使用都是动态的。
在 Java 中可以作为 GC Roots 的对象有以下几种:
OopMap数据结构存储GCRoot对象,但是随着系统的运行会导致OopMap会逐渐变大,所以也并不会存储所有的GCRoot对象,而是在一个所谓的安全点进行记录GCRoot对象。
“标记-清除”(Mark-Sweep)算法
,算法分为“标记”和“清除”两个阶段:
之所以说它是最基础的收集算法,是因为后续的收集算法都是基于这种思路并对其缺点进行改进而得到的。
它的主要缺点有两个:
“复制”(Copying)算法
,它将可用内存按容量划分为大小相等的两块
,每次只使用其中的一块
。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
这样使得每次都是对其中的一块进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。
缺点
复制收集算法在对象存活率较高时就要执行较多的复制操作,效率将会变低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,老年代一般不能直接选用这种算法(老年代一般是存活时间较长的大对象)。
根据老年代的特点,有人提出了另外一种“标记-整理”(Mark-Compact)算法
,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存,这种算法克服了复制算法的低效问题,同时克服了标记清除算法的内存碎片化的问题;
“分代收集”(Generational Collection)算法
,是一种划分的策略,把Java堆分为新生代
和老年代
,
根据各个年代的特点采用最适当的收集算法。
对象存活率低,使用复制算法,分为:Eden区,Survivor区(Survivor from/to)比例为8:1:1
新产生的对象首先进入Eden区,当Eden区满了使用Survivor from当Survivor from满了进行新生代GC(Minior GC),首先将Eden区和Survivor from区存活的对象复制到Survivor to区,然后清空Eden和Survivor from区,此时Survivor from区变成Survivor to区,Survivor to 变成Survivor from区;如果复制的时候发现Survivor to无法容纳全部存活对象,则根据老年代的分配担保将对象copy进老年代,如果老年代无法容纳,则进行Full GC老年代GC。
- 1、大对象直接进入老年代:可根据JVM参数配置-XX:PretenureSizeThreshold,大于配置值即可,这样防止Eden区和Survivor区频繁的内存复制
- 2、长期存活的对象进入老年代:可以根据JVM参数配置年龄,每进行一次GC并且能被Survivor容纳,则年龄+1,如果年龄达到配置值,对象会进入老年代,参数配置:XX:MaxTenuringThreshold;JVM并不是永远要求年龄必须达到最大年龄才会晋升老年代——如果Survivor 空间中相同年龄(如年龄为x)所有对象大小的总和大于Survivor的一半,年龄大于等于x的所有对象直接进入老年代,无需等到最大年龄要求。
为什么要有两个Survivor:
主要是为了解决内存碎片化和效率问题。如果只有一个Survivor时,每触发一次minor gc都会有数据从Eden放到Survivor,一直这样循环下去。注意的是,Survivor区也会进行垃圾回收,这样就会出现内存碎片化问题。
碎片化会导致堆中可能没有足够大的连续空间存放一个大对象,影响程序性能。如果有两块Survivor就能将剩余对象集中到其中一块Survivor上,避免碎片问题。
收集算法是JVM内存回收过程中具体的、通用的方法,垃圾收集器是jvm内存回收过程中具体的执行者,即各种GC算法的具体实现。
串行收集器是最古老,最稳定以及效率高
的收集器,可能会产生较长的停顿
,只使用一个线程去回收。新生代、老年代使用串行回收;
新生代复制算法、老年代标记-整理;
垃圾收集的过程中会Stop The World(服务暂停)
参数控制:**-XX:+UseSerialGC
**串行收集器
ParNew收集器其实就是Serial收集器的多线程版本
。
新生代并行复制算法,老年代串行标记-整理。
参数控制:-XX:+UseParNewGC
ParNew收集器
参数**XX:ParallelGCThreads
** 限制线程数量参数
Parallel Scavenge收集器
类似ParNew收集器,Parallel收集器更关注系统的吞吐量
。可以通过参数来打开自适应调节策略,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或最大的吞吐量;也可以通过参数控制GC的时间不大于多少毫秒或者比例;
新生代复制算法、老年代标记-整理
参数控制:-XX:+UseParallelGC
使用Parallel收集器+ 老年代串行
Serial Old
是Serial收集器
的老年代版本,它同样是一个单线程收集器
,使用“标记-整理”算法
。
这个收集器的主要意义也是在于给Client模式下的虚拟机使用。
如果在Server模式下,那么它主要还有两大用途:
Parallel Old
是Parallel Scavenge收集器的老年代版本
使用多线程和“标记-整理”算法
。这个收集器是在JDK 1.6中才开始提供
参数控制:-XX:+UseParallelOldGC
使用Parallel收集器+ 老年代并行
CMS(Concurrent Mark Sweep)收集器
是一种以获取最短回收停顿时间为目标的收集器
。目前很大一部分的Java应用都集中在互联网站或B/S系统的服务端
上,这类应用尤其重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的体验。
从名字(包含“Mark Sweep”)上就可以看出CMS收集器是基于“标记-清除”算法
实现的,它的运作过程相对于前面几种收集器来说要更复杂一些,整个过程分为4个步骤,包括:
其中初始标记、重新标记这两个步骤仍然需要“Stop The World”
。
初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快
并发标记阶段就是进行GC Roots Tracing追溯查找的过程,
而重新标记阶段则是为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。
由于整个过程中耗时最长的并发标记和并发清除过程中,收集器线程都可以与用户线程一起工作,所以总体上来说,CMS收集器的内存回收过程是与用户线程一起并发地执行。
老年代收集器(新生代使用ParNew)
优点:并发收集、低停顿
缺点:产生大量空间碎片、并发阶段会降低吞吐量
建议:堆内存不要大于8g,最好6g以内。因为CMS+ParNew组合情况下发生的FGC是采用MSC算法且单线程回收,如果堆内存很大,FGC时STW时间会非常恐怖
参数控制:
XX:+UseConcMarkSweepGC
使用CMS收集器-XX:+ UseCMSCompactAtFullCollection
Full GC后,进行一次碎片整理;整理过程是独占的,会引起停顿时间变长XX:+CMSFullGCsBeforeCompaction
设置进行几次Full GC后,进行一次碎片整理XX:ParallelCMSThreads
设定CMS的线程数量(一般情况约等于可用CPU数量)
标记-整理算法
不会产生空间碎片,针对多处理器大容量内存的服务器端的收集器,目标是实现高吞吐量,还能满足垃圾收集暂停时间的要求。
特点:
上面提到的垃圾收集器,收集的范围都是整个新生代或者老年代,而G1不再是这样。使用G1收集器时,Java堆的内存布局与其他收集器有很大差别,它将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔阂了,它们都是一部分(可以不连续)Region的集合。
G1的新生代收集跟ParNew类似,当新生代占用达到一定比例的时候,开始出发收集。和CMS类似,G1收集器收集老年代对象会有短暂停顿。
收集步骤:
JVM默认使用-XX:+UseParallelGC
垃圾收集器,使用Parallel Scavenge
+Parallel Old
收集器组合进行垃圾收集。
JVM内存泄漏和内存溢出的原因
JVM常用监控工具解释以及使用
Redis 常见面试题(一)
ClickHouse之MaterializeMySQL引擎(十)
三种实现分布式锁的实现与区别
线程池的理解以及使用
号外!号外!
最近面试BAT,整理一份面试资料,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。想获取吗?如果你想提升自己,并且想和优秀的人一起进步,感兴趣的朋友,可以在扫码关注下方公众号。资料在公众号里静静的躺着呢。。。
一键四连,你的offer也四连
————————————————————————————————————————————
本文作者:Java技术债务
原文链接:https://www.cuizb.top/myblog/article/1644407284
版权声明: 本博客所有文章除特别声明外,均采用 CC BY 3.0 CN协议进行许可。转载请署名作者且注明文章出处。