花了好几个晚上整理的JVM知识点,吐血献出(一)(下)

简介: 花了好几个晚上整理的JVM知识点,吐血献出(一)(下)

正文


三、垃圾回收机制


什么是垃圾回收


垃圾回收(Garbage Collection,GC),顾名思义就是释放垃圾占用的空间,防止内存泄露。有效的使用可以使用的内存,对内存堆中不可达的对象进行清除和回收。垃圾回收是自动进行回收的,不能人为控制。程序员唯一能做的就是通过调用System.gc() 方法来"建议"执行垃圾收集器,但其是否可以执行,什么时候执行却都是不可知的。


MinorGC和MajorGC


新生代 GC(Minor GC):指发生在新生代的垃圾收集动作,因为 Java 对象大多都具备朝生夕灭的特性,所以 Minor GC 非常频繁,一般回收速度也比较快。



老年代 GC(Major GC  / Full GC):指发生在老年代的 GC,出现了 Major GC,经常会伴随至少一次的 Minor GC(但非绝对的,在 ParallelScavenge 收集器的收集策略里就有直接进行 Major GC 的策略选择过程) 。MajorGC 的速度一般会比 Minor GC 慢 10倍以上。


垃圾判断算法


引用计数器法


引用计数法就是给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值加1;当引用失效时,计数器值减1。任何时刻计数器值为0的对象就是不可能再被使用的。但是这种方法不能判断对象相互引用的这种情况。


根搜索算法


根搜索算法的基本思路就是通过一系列名为”GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可达的。


GC ROOTS主要回收的区域


(1). 虚拟机栈(栈帧中的局部变量区,也叫做局部变量表)中引用的对象。


(2). 方法区中的类静态属性引用的对象。


(3). 方法区中常量引用的对象。


(4). 本地方法栈中JNI(Native方法)引用的对象。


垃圾回收算法


标记-清除


标记-清除包含两部分,标记和清除。一部分标记出可达的对象(有的人认为是标记不可达的对象),然后清除掉不可达的对象。


这种算法的缺点是容易产生不连续的空间碎片,而且标记和清除的效率都不是很高。这种算法适合老年代的对象回收。


复制算法


内存会被分为两部分From区和To区。每次只是使用from区,to区则空闲着。当from区内存不够了,开始执行GC操作,这个时候,会把from区存活的对象拷贝到to区,然后直接把from区进行内存清理。


这种算法的虽然避免了标记-清除碎片化的问题,但是如果回收对象较多较大需要花费更长的时间,而且总会有一部分空间是空闲的,浪费内存空间。这种算法适用于新生代的对象 。


标记-整理(标记-压缩)


标记整理和标记清除算法比较相同,也经过标记阶段,然后把可达对象移动到一端,对不可达的对象进行删除。


这种算法也解决了空间碎片化的问题,但是移动对象,需要修改对象的引用地址,而且标记,整理效率也不高。这种算法适合老年代的对象回收。


分代算法


这种算法,根据对象的存活周期的不同将内存划分成几块,新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。新生代对象朝生夕死,对象数量多,只要重点扫描这个区域,那么就可以大大提高垃圾收集的效率。另外老年代对象存储久,无需经常扫描老年代,避免扫描导致的开销。


新生代使用复制算法,因为新生代中的对象一般都是朝生夕死的,存活对象的数量并不多,这样使用复制算法进行拷贝时效率比较高。jvm将堆内存划分为新生代与老年代,又将新生代划分为Eden与2块Survivor Space,然后在Eden –>Survivor Space 以及From Survivor Space 与To Survivor Space 之间实行复制算法。


111.png


堆空间中新生代和老年代的默认比例是1:2(可以通过参数 –XX:NewRatio)来设定,在新生代中Eden:From:To=8:1:1 (通过参数 –XX:SurvivorRatio )来设定。


复制算法的过程


当Eden区满的时候,会触发第一次MinorGC,把还活着的对象拷贝到Survivor From区。这个时候存活的对象就1岁了。当eden区再次执行MinorGC,就会扫描Eden和From区,把存活的对象复制到To区,然后清空Eden和From区。

当Eden区再次满了之后,再次触发MinorGC,就会扫描Eden和To(新的From区)区,然后将存活的对象复制到From区(新的To区),然后清空Eden和To区。

这样依次往复,在From和To区之间复制来复制去,每熬过一次MinorGC的对象就长大一岁,当对象年满15岁之后,依然存活,则会进入老年代。

可以通过参数设置-XX:MaxTenuringThreshold=15 默认也是15次

注意:这种情况不考虑,破格直接进入老年代的情况。


老年代使用标记清除或者标记整理


老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须“标记-清除-压缩”算法进行回收。


新生代如何进入老年代


创建大对象直接进入老年代 -XX:PretenureSizeThreshold=1M 只对Serial及ParNew收集器管用。


新生代采用的是复制收集算法,S0和S1始终只是用其中一块内存区,当出现MinorGC后大部分对象仍然存活的话,就需要老年代进行空间分配担保,把survior区无法容纳的对象直接晋升到老年代。


长期存活的对象>15岁


当 Survivor 空间中相同年龄(比如10)所有对象的大小总和大于 Survivor 空间的一半,年龄大于或等于该年龄(10)的对象就可以直接进入老年代,而不需要达到MaxTenuringThreshold的分代年龄。


如何触发FullGC


System.gc()方法的调用(大多数的情况下都会进行fullGC,但不能百分之百保证)。

当老年代没有足够空间存放对象时(认为达到92%这个数值仅供参考),会触发一次FullGC。

空间分配担保时,如果剩余空间不足以盛放新生代的对象,这时要进行一次FullGC

如果元空间区域的内存达到了所设定的阈值-XX:MetaspaceSize=,触发FullGC。


内存溢出和内存泄漏


内存溢出(out of memory),是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如系统就分给你10M的空间,你要存放20M的东西,这样就会导致内存溢出。


产生原因:


1.内存中加载的数据量过于庞大,如一次从数据库取出过多数据;


2.集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;


3.代码中存在死循环或循环产生过多重复的对象实体;


4.使用的第三方软件中的BUG;


5.启动参数内存值设定的过小


内存溢出解决方式就是增大jvm的内存


-Xmx3550m -Xms3550m 设置最大内存和初始化内存,两者尽量一致,避免每次垃圾回收完成后JVM重新分配内存。


内存泄露(memory leak),是指程序在申请内存后,无法释放已申请的内存空间。比如一个对象占用了10M的空间,但是它使用完了,一直不释放。如果一次内存泄漏可以容忍,但是有很多的内存泄漏,不管有多少的内存迟早会被占用光,而导致的后果就是,内存溢出。


产生原因:


内存泄露的本质原因是因为代码问题


不使用的对象不能被垃圾回收机制回收。

使用完的资源记得关闭,比如io,数据库等close()。


四、垃圾收集器


垃圾回收器有Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS、G1、ZGC(jdk11之后)。按照新生代和老年代来分,负责新生代的主要是 Serial、ParNew、Parallel Scavenge,负责老年代回收的是Serial Old、Parallel Old、CMS,而G1回收器可以对整个堆进行垃圾回收。


111.png


Serial垃圾收集器


特点:单线程、简单高效(与其他收集器的单线程相比),对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程手机效率。收集器进行垃圾回收时,必须暂停其他所有的工作线程,直到它结束(Stop The World)。使用新生代复制算法。


应用场景:适用于Client模式下的虚拟机。


运行示意图


111.png


ParNew垃圾收集器


和Serial完全一致,除了在收集器使用多线程外。


特点:多线程、ParNew收集器默认开启的收集线程数与CPU的数量相同,在CPU非常多的环境中,可以使用-XX:ParallelGCThreads参数来限制垃圾收集的线程数。


  和Serial收集器一样存在Stop The World问题


应用场景:ParNew收集器是许多运行在Server模式下的虚拟机中首选的新生代收集器,因为它是除了Serial收集器外,唯一一个能与CMS收集器配合工作的。




Parallel Scavenge 垃圾收集器


Parallel Scavenge收集器是一个更关注吞吐量的收集器,与parnew 类似。


特点:属于新生代收集器也是采用复制算法的收集器,又是并行的多线程收集器(与ParNew收集器类似)。


该收集器的目标是达到一个可控制的吞吐量。还有一个值得关注的点是:GC自适应调节策略(与ParNew收集器最重要的一个区别)


GC自适应调节策略:Parallel Scavenge收集器可设置-XX:+UseAdptiveSizePolicy参数。当开关打开时不需要手动指定新生代的大小(-Xmn)、Eden与Survivor区的比例(-XX:SurvivorRation)、晋升老年代的对象年龄(-XX:PretenureSizeThreshold)等,虚拟机会根据系统的运行状况收集性能监控信息,动态设置这些参数以提供最优的停顿时间和最高的吞吐量,这种调节方式称为GC的自适应调节策略。


Parallel Scavenge收集器使用两个参数控制吞吐量:


XX:MaxGCPauseMillis 控制最大的垃圾收集停顿时间

XX:GCRatio 直接设置吞吐量的大小。


Serial Old垃圾收集器


Serial Old是Serial收集器的老年代版本。


特点:同样是单线程收集器,采用标记-整理算法。


应用场景:主要也是使用在Client模式下的虚拟机中。也可在Server模式下使用。


Parallel Old垃圾回收器


是Parallel Scavenge收集器的老年代版本。


特点:多线程,采用标记-整理算法。


应用场景:注重高吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge+Parallel Old 收集器。


CMS收集器


222.png


注意:“标记”是指将存活的对象和要回收的对象都给标记出来,而“清除”是指清除掉将要回收的对象。


其中,初始标记、重新标记这两个步骤仍然需要“Stop The World”。


初始标记只是标记一下GC Roots能直接关联到的对象,速度很快。


并发标记阶段 :并不会阻碍用户线程正常执行任务,与用户线程并发执行进行标记。


重新标记阶段则是为了修正并发标记期间因用户程序继续动作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。


并发清除:对标记的对象进行清除回收。


CMS收集器的缺点:


对CPU资源非常敏感。

无法处理浮动垃圾,可能出现Concurrent Model Failure失败而导致另一次Full GC的产生。

因为采用标记-清除算法所以会存在空间碎片的问题,导致大对象无法分配空间,不得不提前触发一次Full GC。

未完待续


性能调优,实战线上问题排查


。。。。。


参考:


https://blog.csdn.net/qzqanzc/article/details/81008598


JVM内存模型_哦绝影-CSDN博客_内存模型


jvm之java类加载机制和类加载器(ClassLoader)的详解_翻过一座座山-CSDN博客_类加载器


自定义类加载器 - twoheads - 博客园


https://www.cnblogs.com/chenpt/p/9803298.html


相关文章
|
8月前
|
存储 算法 Java
Java虚拟机相关知识点1
Java虚拟机相关知识点
|
7月前
|
缓存 算法 安全
JVM知识点总结
JVM知识点总结
49 0
|
7月前
|
存储 缓存 监控
JVM关键知识点整理,从入门到提高到实践
Java 虚拟机定义了各种在程序执行期间使用的运行时数据区域。这些数据区域有一些是在Java虚拟机启动时创建的,并在Java虚拟机退出时销毁,有一些数据区域是每个线程独有的,在线程创建时创建,在线程销毁时销毁,根据《Java虚拟机规范》的规定,Java虚拟机运行时所需要管理的数据区域主要如下图所示:
290 0
JVM关键知识点整理,从入门到提高到实践
|
8月前
|
存储 缓存 算法
Java虚拟机相关知识点2
Java虚拟机相关知识点
|
8月前
|
存储 缓存 架构师
京东架构师呕心整理:jvm与性能调优有哪些核心技术知识点
相信很多人对于性能调优都不陌生,为了获得更好的系统性能,或者是为了满足不断增加的业务需求。都需要用到我们的性能调优。所以性能优化在面试中出现的频率特别高,这篇文章我主要给大家整理了大厂里面关于jvm和性能调优用到的一些核心技术知识点。
60 0
|
9月前
|
算法 Java 程序员
阿里P8大牛精心整理JVM性能优化知识点+最新JVM面试题(附答案)
JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。它不仅是一种跨平台的软件,而且是一种新的网络计算平台。该平台包括许多相关的技术,如符合开放接口标准的各种API、优化技术等。
|
存储 缓存 Java
JVM内存结构详细总结(结合自身使用经历介绍各个知识点使用场景)
如图JVM运行时数据区域划分为以下6个主要部分:①程序计数器,②虚拟机栈,③本地方法栈,④虚拟机堆,⑤方法区,⑥直接内存,下面对6个部分详细总结,希望可以对路过的朋友有所帮助。
101 0
JVM内存结构详细总结(结合自身使用经历介绍各个知识点使用场景)
|
存储 算法 Java
|
Java
JVM中的一个小知识点:深堆和浅堆的概念
java中的堆内存算是整个内存区域中最重要的一块,几乎所有的对象都分配在堆内存。在堆内存中有两个主要的概念需要我们理解,这对分析java堆内存的故障有着重要的作用,分别是深堆和浅堆。
312 0
JVM中的一个小知识点:深堆和浅堆的概念

相关实验场景

更多