JVM之内存与垃圾回收篇3

文章目录

  • 8 垃圾回收
    • 8.1 基本理论
      • 8.1.1 对象的finalization机制
      • 8.1.2 理解System.gc
      • 8.1.3 内存溢出和内存泄漏
      • 8.1.4 Stop The World
      • 8.1.5 安全点和安全区域
      • 8.1.6 Java中的引用
    • 8.2 垃圾回收算法
      • 8.2.1 引用计数法
      • 8.2.2 可达性分析
        • 8.2.2.1 使用MAT查看GC Roots
        • 8.2.2.2 使用JProfiler分析OOM
      • 8.2.3 复制算法
      • 8.2.4 标记清除
      • 8.2.5 标记压缩
      • 8.2.6 标记清除压缩
      • 8.2.7 小结
      • 8.2.8 增量收集算法
      • 8.2.9 分区算法
    • 8.3 垃圾回收器
      • 8.3.1 评估垃圾回收器的性能指标

8 垃圾回收

8.1 基本理论

【什么是垃圾】
垃圾是指在运行程序中没有任何指针指向的对象。

8.1.1 对象的finalization机制

提供finalization机制来允许开发人员自定义对象销毁前的处理逻辑。 Object中定义了finalize方法,可以被覆写。

永远不要主动调用对象的finalize方法,这个应该交由垃圾收集器调用。 理由如下:
(1)在finalize时可能会导致对象复活。
(2)finalize的执行时刻是没有保障的,完全由GC线程决定。 极端情况下,如果不发生GC,那么finalize方法永远不会执行。
(3)一个糟糕的finalize方法严重影响GC的性能。

虚拟机中的对象存在三种状态:
① 可触及的:从根节点开始,可以到达这个对象
② 可复活的:对象的所有引用都被释放,但是对象有可能在finalize中复活。
③ 不可触及的:对象的finalize被调用,并且没有复活,那么就会进入不可触及状态。 不可触及的对象不可能被复活,因为finalize只会被调用一次。

判定一个对象objA是否可以回收,至少要经历两次标记过程:

  1. 如果对象objA到GC Roots没有引用链,则进行第一次标记。
  2. 进行筛选,判断此对象是否有必要执行finalize方法。
    ① 如果对象objA没有重写finalize方法,或者finalize方法已经被调用过,则虚拟机视为“没有必要执行”,objA被判定为不可触及的。
    ② 如果对象objA重写了finalize方法,并且还未执行,那么objA会被插入到F-Queue队列中,由一个虚拟机自动创建的、低优先级的Finalizer线程触发其finalize方法。
    ③ finalize方法是对象逃脱死亡的最后机会,稍后GC会对F-Queue中的对象进行二次标记。如果objA在finalize方法中与引用链上的任何一个对象建立了联系,那么在二次标记时,objA会被移出“即将回收”集合。之后,如果对象又出现了没有引用存在的情况,对象会直接变为不可触及的状态。 finalize方法只会被调用一次。
/*** 注释掉了finalize方法的结果* first gc* obj is dead* second gc* obj is dead* * 没有注释掉finalize方法的执行结果:* first gc* Call override finalize method.* obj is still alive* second gc* obj is still alive*/
public class CanReliveObj {public static CanReliveObj obj;@Overrideprotected void finalize() throws Throwable {super.finalize();System.out.println("Call override finalize method.");obj = this;}public static void main(String[] args) throws InterruptedException {obj = new CanReliveObj();obj = null;System.gc();System.out.println("first gc");// finalizer线程优先级很低,主线程sleep两秒,等待finalizer执行。Thread.sleep(2000);if (obj == null) {System.out.println("obj is dead");} else {System.out.println("obj is still alive");}System.out.println("second gc");// finalizer线程优先级很低,主线程sleep两秒,等待finalizer执行。Thread.sleep(2000);if (obj == null) {System.out.println("obj is dead");} else {System.out.println("obj is still alive");}}
}

8.1.2 理解System.gc

显示触发full gc,同时对新生代和老年代进行回收。 但是System.gc无法保证对垃圾收集器的调用(无法确保开始执行的时间)。

public class SystemGcTest {public static void main(String[] args) {new SystemGcTest();System.gc();System.runFinalization();}@Overrideprotected void finalize() throws Throwable {super.finalize();System.out.println("SystemGcTest has override finalize() method");}
}

The java.lang.Runtime.runFinalization() method runs the finalization methods of any objects pending finalization. Calling this method suggests that the Java virtual machine expend effort toward running the finalize methods of objects that have been found to be discarded but whose finalize methods have not yet been run. When control returns from the method call, the virtual machine has made a best effort to complete all outstanding finalizations.

The virtual machine performs the finalization process automatically as needed, in a separate thread, if the runFinalization method is not invoked explicitly. The method System.runFinalization() is the conventional and convenient means of invoking this method.

8.1.3 内存溢出和内存泄漏

【内存溢出】
OOM:没有空闲的内存,并且垃圾收集器也无法提供更多的内存。

【内存泄漏】
内存泄漏:只有对象不会再被程序用到了,但是GC又不能回收他们的情况,称为内存泄漏。
实际情况中,可能会存在一些不好的实现,会导致对象的生命周期变得很长,甚至导致了OOM,这种就叫做广义上的“内存泄漏”。

举例:

  1. 单例模式
    如果单例对象持有外部对象的引用的话,这个外部对象是不能被回收的,会导致内存泄漏产生。

  2. 一些提供close的资源未关闭导致内存泄漏
    数据库连接、网络连接和IO连接必须手动close,否则是不会被回收的。

8.1.4 Stop The World

8.1.5 安全点和安全区域

用户线程只有在特定的位置才能停顿下来GC,这些位置称为“安全点(Safe Point)”。如果安全点太少会导致GC等待的时间太长。如果太多会导致运行时性能问题。通常会选择一些执行时间较长的指令作为safe point,如方法调用、循环跳转和异常跳转。

【如何保证在GC的时候,检查所有线程都跑到最近的安全点上停顿下来】

  1. 抢先式中断
    目前没有虚拟机采用了。
    首先中断所有线程。如果还有线程不在安全点,就恢复线程,让线程跑到安全点。
  2. 主动式中断
    设置一个中断标志,各个线程运行到安全点的时候主动轮询这个标志,如果中断标志为true,则将自己挂起。

线程处于Sleep状态或者Blocked状态,这时候无法响应JVM的中断请求,无法走到安全点。此种情况下,需要靠安全区域来解决这个问题。
安全区是指在一段代码片段中,对象的引用关系不会发生变化,在这个区域中任何位置开始GC都是安全的。

【安全区域的运作机制】

  1. 当线程运行到safe region的时候,标识自己已经进入了Safe Region,如果这段时间内发生了GC,JVM会忽略标识为Safe Region状态的线程。
  2. 当线程即将离开Safe Region时,会检查JVM是否已经完成了GC,如果完成了GC,继续运行。否则线程必须等待直到收到可以安全离开Safe Region的信号为止。

8.1.6 Java中的引用

强引用:不回收
软引用:内存不足即回收
弱引用:发现即回收
虚引用:对象回收跟踪
【终结器引用】
终结器引用用来实现对象的finalize方法。
无需手动编码,其内部配合引用队列使用。
在GC时,终结器引用入队。由Finalizer线程通过终结器引用找到被引用对象并调用它的finalize方法,第二次gc的时候才能回收被引用对象。

8.2 垃圾回收算法

在这里插入图片描述

8.2.1 引用计数法

【怎样判断垃圾——引用记数法】
在这里插入图片描述
优点:实现简单,垃圾对象便于识别;判定效率高,回收没有延迟性(只要引用计数是0,随时都可以回收)。

Python使用的是引用计数法。Python解决循环引用的方法:(1)手动接触引用(2)弱引用。

8.2.2 可达性分析

又名根搜索算法,追踪性垃圾收集算法。
在这里插入图片描述

【可以作为GC Roots的对象】

  • 虚拟机栈中引用的对象
    方法的参数,局部变量等。
  • 本地方法栈中引用的对象
  • 方法区中类静态属性引用的对象
    Java应用类型的静态变量
  • 方法区中常量引用的对象
    字符串常量池中的引用
  • 所有被同步锁synchorinized持有的对象
  • Java虚拟机内部的引用
    基本数据类型对应的Class对象,一些常驻的异常对象(如NPE,OOM),系统类加载器。
  • 反应虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。

【Stop The World】
可达性分析必须要在一个能保障一致性的快照中进行。 如果不满足的话,分析结果的准确性无法保证。 这就是GC进行时必须“Stop the World”的一个重要原因。 即使几乎不发生停顿的CMS收集器中,枚举根节点时也是必须要停顿的。

8.2.2.1 使用MAT查看GC Roots

① 待查看的代码

package org.example.gcroot;import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Scanner;public class GcRootTest {public static void main(String[] args) {List<Object> numList = new ArrayList<>();Date birth = new Date();for (int i = 0; i < 100; i++) {numList.add(String.valueOf(i));try {Thread.sleep(10);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("数据添加完毕,请操作");new Scanner(System.in).next();numList = null;birth = null;System.out.println("numList, birth已经置空,请操作");new Scanner(System.in).next();System.out.println("程序结束");}
}

② 在运行过程中使用visualvm导出堆dump,在第一次程序阻塞和第二次程序阻塞的时候都dump一次
在这里插入图片描述
③ 使用MAT查看两次导出的dump文件,查看gc roots
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

8.2.2.2 使用JProfiler分析OOM

① 启用在OOM的时候导出HeapDump
在这里插入图片描述
② 使用JProfiler查看Biggest Object
在这里插入图片描述
③ 查看Thread Dump,查看哪个线程的哪一行有问题。
在这里插入图片描述

8.2.3 复制算法

Copying

【优点】
实现简单,运行高效。
可以保证空间连续性,不会出现“碎片”问题。
【缺点】
需要两倍的内存空间。空间浪费。
G1将内存拆分成很多的region,在复制过程中,GC需要维护region之间对象的引用关系。内存占用和时间开销都是不小的。

如果系统中存活的对象很多,复制算法就不理想。这样会复制很多存活的对象,过犹不及。

8.2.4 标记清除

在这里插入图片描述
在这里插入图片描述
标记:Collector从引用根节点开始遍历,标记所有被引用的对象。一般是在对象的Header中记录为可达对象。 注意:标记清除算法的标记标记的是可达对象,不是垃圾对象。
清除:Collector对堆内存进行线性遍历,如果发现某个对象的Header中没有可达标记,则将其回收。

缺点:
效率不算高。 两次扫描
在GC的时候需要停止整个应用程序。
存在内存碎片,需要维护空闲列表。

【何为清除?】
清除并不是真的置空,而是把要清除的对象地址保存在空闲的地址列表里面。下次有新对象需要加载时,判断垃圾的位置空间是否够,如果足够就存放。

8.2.5 标记压缩

标记压缩算法又叫标记整理算法。
在这里插入图片描述
【优点】

  • 没有内存碎片
  • 没有复制算法内存减半的高额代价。
    【缺点】
  • 效率上讲,低于复制算法
  • 需要调整引用的地址
  • STW

8.2.6 标记清除压缩

在这里插入图片描述

8.2.7 小结

在这里插入图片描述
在这里插入图片描述
Mark阶段的开销与存活对象的数量成正比。
Sweep阶段的开销与所管辖的区域的大小成正比。
Compact阶段的开销与存活对象的数据成正比。

以HotSpot中的CMS回收器为例,CMS是基于MARK-SWEEP实现的,对于对象的回收效率很高。对于碎片问题,CMS使用基于标记压缩算法的Serial Old收集器进行补偿。 当内存回收不佳的时候,将采用Serial Old执行Full GC以达到对老年代内存的整理。

8.2.8 增量收集算法

优点:
缺点:系统吞吐量的下降。

8.2.9 分区算法

将大的内存区域分割成多个小块,根据目标的停顿时间,每次合理地回收若干个小区间,而非整个堆空间。 从而减少一次GC所产生的停顿。

8.3 垃圾回收器

8.3.1 评估垃圾回收器的性能指标

【吞吐量】
运行用户代码时间占总运行时间的比例。
总运行时间 = 程序的运行时间 + 内存回收的时间
【垃圾收集开销】
吞吐量的补数,垃圾收集所用时间占总运行时间的比例。
【暂停时间】
进行垃圾收集时,程序的工作线程被暂停的时间
【收集频率】
收集操作发生的频率
【内存占用】
Java堆区所占内存的大小。
【回收速率】
一个对象从诞生到被回收所经历的时间。

串行回收器:Serial、Serial Old
并行回收器:ParNew、Parallel Scavenge、Parallel Old
并发回收器:CMS、G1

新生代收集器:Serial、ParNew、 Parallel Scavenge
老年代收集器:Serial Old、Parallel Old、CMS
整堆收集器:G1

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/5473.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

整车总线系列——FlexRay 四

整车总线系列——FlexRay 四 我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 没有人关注你。也无需有人关注你。你必须承认自己的价值&#xff0c;你不能…

Spring使用注解存储Bean对象

文章目录 一. 配置扫描路径二. 使用注解储存Bean对象1. 使用五大类注解储存Bean2. 为什么要有五大类注解&#xff1f;3.4有关获取Bean参数的命名规则 三. 使用方法注解储存Bean对象1. 方法注解储存对象的用法2. Bean的重命名 在前一篇博客中&#xff08; Spring项目创建与Bean…

nginx 配置 wss加密访问 mqtt

1. 在服务器上部署mqtt服务 2.在宝塔上配置域名证书 3.nginx配置websocket server {listen 80;listen 443 ssl http2;server_name ws-xx.example.com;index index.php index.html index.htm default.php default.htm default.html;root /www/wwwroot/ws-xx.example.com;loca…

服务器量化训练操作说明

Freespace服务器预训练主要步骤&#xff1a; 首先登录堡垒机&#xff0c;命令如下&#xff1a; ssh xxxrelay.baidu-int.com &#xff08;xxx为个人邮箱前缀&#xff09; 密码为个人邮箱密码 登录工作机&#xff0c;命令如下&#xff1a; ssh l3yq01-gpu-255-122-22-00.e…

如何在小程序首页隐藏商品分类

​因为在分类页面有显示分类&#xff0c;所以有的商家希望在首页就不显示分类啦。下面就介绍一下如何在首页隐藏商品分类。 在小程序管理员后台->页面设置->首页&#xff0c;显示商品分类设置为否。设置后&#xff0c;在小程序首页商品分类就不会显示啦。注意&#xff1a…

django rest_framework 框架动态设置序列化返回的字段

动态修改字段可以使Django rest框架API像graphQL端点一样&#xff0c;只从模型中检索所需的字段。 一旦序列化器被初始化&#xff0c;就可以使用.fields属性访问序列化器上设置的字段字典。访问和修改此属性允许您动态修改序列化器。 显式地修改fields参数可以帮助您做一些奇…

关于Integer类的一个有趣的面试问题

相信很多人觉得答案是false&#xff0c;false&#xff0c;因为Integer是一个类&#xff0c;把int类型的数据传给Integer类型的数据会创建一个对象&#xff0c;而a,b,c,d作为引用指向的是不同的地址&#xff0c;所以判断相同得到的结果应该是false 但这个想法就正中下怀了&#…

【Spring篇】初识 Spring IoC 与 DI

目录 一. Spring 是什么 ? 二. 何为 IoC ? 三. 如何理解 Spring IoC ? 四. IoC 与 DI 五 . 总结 一. Spring 是什么 ? 我们通常所说的 Spring 指的是 Spring Framework&#xff08;Spring 框架&#xff09;&#xff0c;它是⼀个开源框架&#xff0c;有着活跃⽽ 庞⼤…

探秘Spring中Bean的注解宝典:解读存取Bean的相关注解及用法

目录 存储Bean对象Controller(控制器存储)Service(服务存储)Repository(仓库存储)Component(组件存储)Configuration(配置存储)Bean重命名Bean 获取Bean对象属性注入构造方法注入Setter注入Resource(注入关键字) 存储Bean对象 将对象存储在 Spring 中&#xff0c;有两种注解类…

【Linux】信号保存信号处理

前言&#xff1a;对信号产生的思考 上一篇博客所说的信号产生&#xff0c;最终都要有OS来进行执行&#xff0c;为什么&#xff1f;OS是进程的管理者&#xff01;信号的处理是否是立即处理的&#xff1f;在合适的时候 -》那什么是合适的时候&#xff1f;信号如图不是被立即处理…

Windows安装RabbitMQ

Windows安装RabbitMQ 前言配置erlang环境下载配置环境变量验证 安装RabbitMQ验证 参考 前言 本文并不涉及到RabbitMQ的底层原理&#xff0c;或者别的一些特性说明&#xff0c;仅仅只是安装。 配置erlang环境 因为RabbitMQ是使用该语言开发的。 下载 下载地址&#xff1a; …

《qt quick核心编程》笔记一

1.基础HelloWorld代码 import QtQuick 2.15 import QtQuick.Window 2.15 import QtQuick.Controls 2.15Window {width: 400height: 300visible: truetitle: qsTr("Hello 1World")Rectangle {width: parent.widthheight: parent.heightcolor: "gray"Text {…

【数据挖掘】bytewax 与 ydata工具可实时了解您的数据

一、说明 在这篇博文中&#xff0c;我们将介绍如何将开源流式处理解决方案 bytewax 与 ydata 分析相结合并加以利用&#xff0c;以提高流式处理流的质量。 STream 处理支持在传输中和存储之前对数据进行实时分析&#xff0c;并且可以是有状态的&#xff0c;也可以是无状态的。 …

【网络编程】网络套接字udp通用服务器和客户端

1.预备知识 认识端口号 端口号(port)是传输层协议的内容&#xff1a; 端口号是一个2字节16位的整数(uint16)端口号用来标识主机上的一个进程IP地址port能够标识网络上的某一台主机和某一个进程一个端口号只能被一个进程占用 认识TCP协议 此处我们先对TCP(Transmission Con…

Spring MVC异步上传、跨服务器上传和文件下载

一、异步上传 之前的上传方案&#xff0c;在上传成功后都会跳转页面。而在实际开发中&#xff0c;很多情况下上传后不进行跳转&#xff0c;而是进行页面的局部刷新&#xff0c;比如&#xff1a;上传头像成功后将头像显示在网页中。这时候就需要使用异步文件上传。 1.1 JSP页面 …

[golang gin框架] 41.Gin商城项目-微服务实战之后台Rbac微服务(用户登录 、Gorm数据库配置单独抽离、 Consul配置单独抽离)

上一节抽离了captcha验证码功能,集成了验证码微服务功能,这一节来看看后台Rbac功能,并抽离其中的用户登录,管理员管理,角色管理,权限管理等功能作为微服务来调用 一.引入 后台操作从登录到后台首页,然后其中的管理员管理,角色管理,权限管理等功能可以抽离出来作为 一个Rbac微服…

Python实战

官方文档 请点击下面工程名称&#xff0c;跳转到代码的仓库页面&#xff0c;将工程 下载下来 Demo Code 里有详细的注释 LearnPythonPython 实现功能点demo

OpenCV for Python 实战(一):获取图片拍摄GPS地址并自动添加水印

Hello 我们在OpenCV每天的基础博客当中已经更新了很多了&#xff0c;那么今天我们就来结合前几天的内容。做一个获取属性然后添加对应属性的水印。那让我们赶快开始吧~ 文章目录 图片EXIFPython 获取EXIFexifread库使用方法转换成文字地址 添加水印cv2.putText() 每日总结 图片…

【001 操作系统】什么是线程、进程?线程进程的区别是什么?

一、什么是线程、进程&#xff1f; 进程&#xff1a;进程是资源分配的基本单位&#xff0c;它是程序执行时的一个实例&#xff0c;在程序运行时创建。 在Linux环境下&#xff0c;每个进程有自己各自独立的 4G 地址空间&#xff0c;大家互不干扰对方&#xff0c;如果两个进程之间…

基于大模型的Text2SQL微调的实战教程

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…