有哪些常见的垃圾回收算法? ⚫1960年John McCarthy发布了第一个GC算法:标记-清除算法。 ⚫1963年Marvin L. Minsky 发布了复制算法。 本质上后续所有的垃圾回收算法,都是在上述两种算法的基础上优化而来。 垃圾回收算法-标记清除算法 标记清除算法的核心思想分为两个阶段: 1.标记阶段,将所有存活的对象进行标记。Java中使用可达性分析算法,从GC Root开始通过引用链遍历出 所有存活对象。 2.清除阶段,从内存中删除没有被标记也就是非存活对象。 垃圾回收算法-标记清除算法的优缺点 优点:实现简单,只需要在第一阶段给每个对象维护标志位,第二阶段删除对象即可。 缺点:1.碎片化问题 由于内存是连续的,所以在对象被删除之后,内存中会出现很多细小的可用内存单元。如果我们需要的是一个比较大的空间,很有可能这些内存单元的大小过小无法进行分配 2.分配速度慢。由于内存碎片的存在,需要维护一个空闲链表,极有可能发生每次需要遍历到链表的最后才能获得合适的内存空间。 垃圾回收算法-复制算法 复制算法的核心思想是: 1.准备两块空间From空间和To空间,每次在对象分配阶段,只能使用其中一块空间(From空间)。 2.在垃圾回收GC阶段,将From中存活对象复制到To空间。 3.将两块空间的From和To名字互换。 垃圾回收算法-标记整理算法 标记整理算法也叫标记压缩算法,是对标记清理算法中容易产生内存碎片问题的一种解决方案。 核心思想分为两个阶段: 1.标记阶段,将所有存活的对象进行标记。Java中使用可达性分析算法,从GC Root开始通过引用链遍历出 所有存活对象。 2.整理阶段,将存活对象移动到堆的一端。清理掉存活对象的内存空间。 垃圾回收算法-分代垃圾回收算法 现代优秀的垃圾回收算法,会将上述描述的垃圾回收算法组合进行使用,其中应用最广的就是分代垃圾回收 算法(Generational GC)。 分代垃圾回收将整个内存区域划分为年轻代和老年代: 垃圾回收算法-分代垃圾回收算法 分代回收时,创建出来的对象,首先会被放入Eden伊甸园区。 随着对象在Eden区越来越多,如果Eden区满,新创建的对象已经无法放入,就会触发年轻代的GC,称为 Minor GC或者Young GC。 Minor GC会把需要eden中和From需要回收的对象回收,把没有回收的对象放入To区。 接下来,S0会变成To区,S1变成From区。当eden区满时再往里放入对象,依然会发生Minor GC。 此时会回收eden区和S1(from)中的对象,并把eden和from区中剩余的对象放入S0。 注意:每次Minor GC中都会为对象记录他的年龄,初始值为0,每次GC完加1。 如果Minor GC后对象的年龄达到阈值(最大15,默认值和垃圾回收器有关),对象就会被晋升至老年代。 当老年代中空间不足,无法放入新的对象时,先尝试minor gc如果还是不足,就会触发Full GC,Full GC会对整个 堆进行垃圾回收。 如果Full GC依然无法回收掉老年代的对象,那么当对象继续放入老年代时,就会抛出Out Of Memory异常。 垃圾回收算法-分代垃圾回收算法优点 程序中大部分对象都是朝生夕死,在年轻代创建并且回收,只有少量对象会长期存活进入老年代。分代垃圾 回收的优点有: 1、可以通过调整年轻代和老年代的比例来适应不同类型的应用程序,提高内存的利用率和性能。 2、新生代和老年代使用不同的垃圾回收算法,新生代一般选择复制算法效率高、不会产生内存碎片,老年 代可以选择标记-清除和标记-整理算法,由程序员来选择灵活度较高。 3、分代的设计中允许只回收新生代(minor gc),如果能满足对象分配的要求就不需要对整个堆进行回收(full gc),STW(Stop The World)由垃圾回收引起的停顿时间就会减少。

有哪些常用的垃圾回收器

垃圾回收器是垃圾回收算法的具体实现。 由于垃圾回收器分为年轻代和老年代,除了G1之外其他垃圾回收器必须成对组合进行使用。 具体的关系图如下: 垃圾回收器的组合关系虽然很多,但是针对几个特定的版本,比较好的组合选 择如下: JDK8及之前: ParNew + CMS(关注暂停时间)、Parallel Scavenge + Parallel Old (关注 吞吐量)、 G1(JDK8之前不建议,较大堆并且关注暂停时间) JDK9之后: G1(默认) 从JDK9之后,由于G1日趋成熟,JDK默认的垃圾回收器已经修改为G1,所以 强烈建议在生产环境上使用G1。 如果对低延迟有较高的要求,可以使用Shenandoah或者ZGC。

如何解决内存泄漏问题

什么是内存泄漏,如何解决内存泄漏问题? ⚫ 内存泄漏(memory leak):在Java中如果不再使用一个对象,但是该对象依然在GC ROOT的引用链上,这个对象就不会被垃圾回收器回收,这种情况就称之为内存泄漏。 ⚫ 少量的内存泄漏可以容忍,但是如果发生持续的内存泄漏,就像滚雪球雪球越滚越大,不管有多大的内存迟早会被消耗完,最终导致的结果就是内存溢出。 解决内存泄漏问题的思路 发现问题 – 堆内存状况的对比 生产环境通过运维提供的Prometheus + Grafana等监控平台查看 开发、测试环境通过visualvm查看

诊断 – 生成内存快照 ⚫ 当堆内存溢出时,需要在堆内存溢出时将整个堆内存保存下来,生成内存快照(Heap Profile )文件。 生成方式有两种 1、内存溢出时自动生成,添加生成内存快照的Java虚拟机参数: -XX:+HeapDumpOnOutOfMemoryError:发生OutOfMemoryError错误时,自动生成hprof内存快照文件。 -XX:HeapDumpPath=

:指定hprof文件的输出路径。

2、导出运行中系统的内存快照,比较简单的方式有两种,注意只需要导出标记为存活的对象:

通过JDK自带的jmap命令导出,格式为:

jmap -dump:live,format=b,file=文件路径和文件名 进程ID

通过arthas的heapdump命令导出,格式为:

heapdump --live 文件路径和文件名

诊断 – MAT定位问题 ⚫ 使用MAT打开hprof文件,并选择内存泄漏检测功能,MAT会自行根据内存快照中保存的数据分析内存泄漏的根源 修复问题 修复内存溢出问题的要具体问题具体分析,问题总共可以分成三类 常用的JVM工具 JDK自带的命令行工具: ◆ jps 查看java进程,打印main方法所在类名和进程id ◆ jmap 1、生成堆内存快照 2、打印类的直方图 第三方工具: VisualVM 监控 Arthas 综合性工具 MAT 堆内存分析工具 监控工具: Prometheus + grafana

常见的JVM参数

常见的JVM参数 ◼ 参数1 : -Xmx 和 –Xms -Xmx参数设置的是最大堆内存,但是由于程序是运行在服务器或者容器上,计算可用内存时,要将元空间、操作系统、 其它软件占用的内存排除掉。 案例: 服务器内存4G,操作系统+元空间最大值+其它软件占用1.5G,-Xmx可以设置为2g。 最合理的设置方式应该是根据最大并发量估算服务器的配置,然后再根据服务器配置计算最大堆内存的值。 建议将-Xms设置的和-Xmx一样大,运行过程中不再产生扩容的开销。 ◼ 参数2 : -XX:MaxMetaspaceSize 和 -Xss -XX:MaxMetaspaceSize=值 参数指的是最大元空间大小,默认值比较大,如果出现元空间内存泄漏会让操作系 统可用内存不可控,建议根据测试情况设置最大值,一般设置为256m。 -Xss256k 栈内存大小,如果我们不指定栈的大小,JVM 将创建一个具有默认大小的栈。大小取决于操作系统和计 算机的体系结构。比如Linux x86 64位 : 1MB,如果不需要用到这么大的栈内存,完全可以将此值调小节省内存 空间,合理值为256k – 1m之间。

◼ 参数3: ➢ -Xmn 年轻代的大小,默认值为整个堆的1/3,可以根据峰值流量计算最大的年轻代大小,尽量让对象只存放在年 轻代,不进入老年代。但是实际的场景中,接口的响应时间、创建对象的大小、程序内部还会有一些定时任务等不 确定因素都会导致这个值的大小并不能仅凭计算得出,如果设置该值要进行大量的测试。G1垃圾回收器尽量不要设 置该值,G1会动态调整年轻代的大小。 常见的JVM参数 ◆ 打印GC日志 JDK8及之前 : -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:文件路径 JDK9及之后 : -Xlog:gc*:file=文件路径 ◆ -XX:+DisableExplicitGC 禁止在代码中使用System.gc(), System.gc()可能会引起FULLGC,在代码中尽量不要使用。使用 DisableExplicitGC参数可以禁止使用System.gc()方法调用。 ◆ -XX:+HeapDumpOnOutOfMemoryError:发生OutOfMemoryError错误时,自动生成hprof内存快照文件。 -XX:HeapDumpPath=

:指定hprof文件的输出路径。

解决问题 - 优化基础JVM参数 JVM参数模板: 注意: JDK9及之后gc日志输出修改为 -Xlog:gc*:file=文件名 堆内存大小和栈内存大小根据实际情况灵活调整。

文章链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: