JVM垃圾回收与性能调优策略

news2024/11/15 13:49:01

c35bf0bc416b4b92ad3e728483e8ba28.jpg一、JVM内存模型及垃圾收集算法

 

 

 1.根据Java虚拟机规范,JVM将内存划分为:

 

New(年轻代)

Tenured(年老代)

永久代(Perm)

  其中New和Tenured属于堆内存,堆内存会从JVM启动参数(-Xmx:3G)指定的内存中分配,Perm不属于堆内存,有虚拟机直接分配,但可以通过-XX:PermSize -XX:MaxPermSize 等参数调整其大小。

 

 

 

年轻代(New):年轻代用来存放JVM刚分配的Java对象

年老代(Tenured):年轻代中经过垃圾回收没有回收掉的对象将被Copy到年老代

永久代(Perm):永久代存放Class、Method元信息,其大小跟项目的规模、类、方法的量有关,一般设置为128M就足够,设置原则是预留30%的空间。

New又分为几个部分:

 

Eden:Eden用来存放JVM刚分配的对象

Survivor1

Survivro2:两个Survivor空间一样大,当Eden中的对象经过垃圾回收没有被回收掉时,会在两个Survivor之间来回Copy,当满足某个条件,比如Copy次数,就会被Copy到Tenured。显然,Survivor只是增加了对象在年轻代中的逗留时间,增加了被垃圾回收的可能性。

 2.垃圾回收算法

 

  垃圾回收算法可以分为三类,都基于标记-清除(复制)算法:

 

Serial算法(单线程)

并行算法

并发算法

  JVM会根据机器的硬件配置对每个内存代选择适合的回收算法,比如,如果机器多于1个核,会对年轻代选择并行算法,关于选择细节请参考JVM调优文档。

 

  稍微解释下的是,并行算法是用多线程进行垃圾回收,回收期间会暂停程序的执行,而并发算法,也是多线程回收,但期间不停止应用执行。所以,并发算法适用于交互性高的一些程序。经过观察,并发算法会减少年轻代的大小,其实就是使用了一个大的年老代,这反过来跟并行算法相比吞吐量相对较低。

 

 

 

  还有一个问题是,垃圾回收动作何时执行?

 

当年轻代内存满时,会引发一次普通GC,该GC仅回收年轻代。需要强调的时,年轻代满是指Eden代满,Survivor满不会引发GC

当年老代满时会引发Full GC,Full GC将会同时回收年轻代、年老代

当永久代满时也会引发Full GC,会导致Class、Method元信息的卸载

  另一个问题是,何时会抛出OutOfMemoryException,并不是内存被耗空的时候才抛出

 

JVM98%的时间都花费在内存回收

每次回收的内存小于2%

  满足这两个条件将触发OutOfMemoryException,这将会留给系统一个微小的间隙以做一些Down之前的操作,比如手动打印Heap Dump。

 

 

 

二、内存泄漏及解决方法

 

 1.系统崩溃前的一些现象:

 

每次垃圾回收的时间越来越长,由之前的10ms延长到50ms左右,FullGC的时间也有之前的0.5s延长到4、5s

FullGC的次数越来越多,最频繁时隔不到1分钟就进行一次FullGC

年老代的内存越来越大并且每次FullGC后年老代没有内存被释放

 之后系统会无法响应新的请求,逐渐到达OutOfMemoryError的临界值。

 

 

 

 2.生成堆的dump文件

 

 通过JMX的MBean生成当前的Heap信息,大小为一个3G(整个堆的大小)的hprof文件,如果没有启动JMX可以通过Java的jmap命令来生成该文件。

 

 

 

 3.分析dump文件

 

 下面要考虑的是如何打开这个3G的堆信息文件,显然一般的Window系统没有这么大的内存,必须借助高配置的Linux。当然我们可以借助X-Window把Linux上的图形导入到Window。我们考虑用下面几种工具打开该文件:

 

Visual VM

IBM HeapAnalyzer

JDK 自带的Hprof工具

 使用这些工具时为了确保加载速度,建议设置最大内存为6G。使用后发现,这些工具都无法直观地观察到内存泄漏,Visual VM虽能观察到对象大小,但看不到调用堆栈;HeapAnalyzer虽然能看到调用堆栈,却无法正确打开一个3G的文件。因此,我们又选用了Eclipse专门的静态内存分析工具:Mat。

 

 

 

 4.分析内存泄漏

 

 通过Mat我们能清楚地看到,哪些对象被怀疑为内存泄漏,哪些对象占的空间最大及对象的调用关系。针对本案,在ThreadLocal中有很多的JbpmContext实例,经过调查是JBPM的Context没有关闭所致。

 

 另,通过Mat或JMX我们还可以分析线程状态,可以观察到线程被阻塞在哪个对象上,从而判断系统的瓶颈。

 

 

 

 5.回归问题

 

   Q:为什么崩溃前垃圾回收的时间越来越长?

 

   A:根据内存模型和垃圾回收算法,垃圾回收分两部分:内存标记、清除(复制),标记部分只要内存大小固定时间是不变的,变的是复制部分,因为每次垃圾回收都有一些回收不掉的内存,所以增加了复制量,导致时间延长。所以,垃圾回收的时间也可以作为判断内存泄漏的依据

 

   Q:为什么Full GC的次数越来越多?

 

   A:因此内存的积累,逐渐耗尽了年老代的内存,导致新对象分配没有更多的空间,从而导致频繁的垃圾回收

 

   Q:为什么年老代占用的内存越来越大?

 

   A:因为年轻代的内存无法被回收,越来越多地被Copy到年老代

 

 

 

三、性能调优

 

 除了上述内存泄漏外,我们还发现CPU长期不足3%,系统吞吐量不够,针对8core×16G、64bit的Linux服务器来说,是严重的资源浪费。

 

 在CPU负载不足的同时,偶尔会有用户反映请求的时间过长,我们意识到必须对程序及JVM进行调优。从以下几个方面进行:

 

线程池:解决用户响应时间长的问题

连接池

JVM启动参数:调整各代的内存比例和垃圾回收算法,提高吞吐量

程序算法:改进程序逻辑算法提高性能

  1.Java线程池(java.util.concurrent.ThreadPoolExecutor)

 

    大多数JVM6上的应用采用的线程池都是JDK自带的线程池,之所以把成熟的Java线程池进行罗嗦说明,是因为该线程池的行为与我们想象的有点出入。Java线程池有几个重要的配置参数:

 

corePoolSize:核心线程数(最新线程数)

maximumPoolSize:最大线程数,超过这个数量的任务会被拒绝,用户可以通过RejectedExecutionHandler接口自定义处理方式

keepAliveTime:线程保持活动的时间

workQueue:工作队列,存放执行的任务

    Java线程池需要传入一个Queue参数(workQueue)用来存放执行的任务,而对Queue的不同选择,线程池有完全不同的行为:

 

SynchronousQueue: 一个无容量的等待队列,一个线程的insert操作必须等待另一线程的remove操作,采用这个Queue线程池将会为每个任务分配一个新线程

LinkedBlockingQueue : 无界队列,采用该Queue,线程池将忽略 maximumPoolSize参数,仅用corePoolSize的线程处理所有的任务,未处理的任务便在LinkedBlockingQueue中排队

ArrayBlockingQueue: 有界队列,在有界队列和 maximumPoolSize的作用下,程序将很难被调优:更大的Queue和小的maximumPoolSize将导致CPU的低负载;小的Queue和大的池,Queue就没起动应有的作用。

    其实我们的要求很简单,希望线程池能跟连接池一样,能设置最小线程数、最大线程数,当最小数<任务<最大数时,应该分配新的线程处理;当任务>最大数时,应该等待有空闲线程再处理该任务。

 

    但线程池的设计思路是,任务应该放到Queue中,当Queue放不下时再考虑用新线程处理,如果Queue满且无法派生新线程,就拒绝该任务。设计导致“先放等执行”、“放不下再执行”、“拒绝不等待”。所以,根据不同的Queue参数,要提高吞吐量不能一味地增大maximumPoolSize。

 

    当然,要达到我们的目标,必须对线程池进行一定的封装,幸运的是ThreadPoolExecutor中留了足够的自定义接口以帮助我们达到目标。我们封装的方式是:

 

以SynchronousQueue作为参数,使maximumPoolSize发挥作用,以防止线程被无限制的分配,同时可以通过提高maximumPoolSize来提高系统吞吐量

自定义一个RejectedExecutionHandler,当线程数超过maximumPoolSize时进行处理,处理方式为隔一段时间检查线程池是否可以执行新Task,如果可以把拒绝的Task重新放入到线程池,检查的时间依赖keepAliveTime的大小。

  2.连接池(org.apache.commons.dbcp.BasicDataSource)

 

    在使用org.apache.commons.dbcp.BasicDataSource的时候,因为之前采用了默认配置,所以当访问量大时,通过JMX观察到很多Tomcat线程都阻塞在BasicDataSource使用的Apache ObjectPool的锁上,直接原因当时是因为BasicDataSource连接池的最大连接数设置的太小,默认的BasicDataSource配置,仅使用8个最大连接。

 

    我还观察到一个问题,当较长的时间不访问系统,比如2天,DB上的Mysql会断掉所以的连接,导致连接池中缓存的连接不能用。为了解决这些问题,我们充分研究了BasicDataSource,发现了一些优化的点:

 

Mysql默认支持100个链接,所以每个连接池的配置要根据集群中的机器数进行,如有2台服务器,可每个设置为60

initialSize:参数是一直打开的连接数

minEvictableIdleTimeMillis:该参数设置每个连接的空闲时间,超过这个时间连接将被关闭

timeBetweenEvictionRunsMillis:后台线程的运行周期,用来检测过期连接

maxActive:最大能分配的连接数

maxIdle:最大空闲数,当连接使用完毕后发现连接数大于maxIdle,连接将被直接关闭。只有initialSize < x < maxIdle的连接将被定期检测是否超期。这个参数主要用来在峰值访问时提高吞吐量。

initialSize是如何保持的?经过研究代码发现,BasicDataSource会关闭所有超期的连接,然后再打开initialSize数量的连接,这个特性与minEvictableIdleTimeMillis、timeBetweenEvictionRunsMillis一起保证了所有超期的initialSize连接都会被重新连接,从而避免了Mysql长时间无动作会断掉连接的问题。

  3.JVM参数

 

    在JVM启动参数中,可以设置跟内存、垃圾回收相关的一些参数设置,默认情况不做任何设置JVM会工作的很好,但对一些配置很好的Server和具体的应用必须仔细调优才能获得最佳性能。通过设置我们希望达到一些目标:

 

GC的时间足够的小

GC的次数足够的少

发生Full GC的周期足够的长

  前两个目前是相悖的,要想GC时间小必须要一个更小的堆,要保证GC次数足够少,必须保证一个更大的堆,我们只能取其平衡。

 

   (1)针对JVM堆的设置,一般可以通过-Xms -Xmx限定其最小、最大值,为了防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间,我们通常把最大、最小设置为相同的值

   (2)年轻代和年老代将根据默认的比例(1:2)分配堆内存,可以通过调整二者之间的比率NewRadio来调整二者之间的大小,也可以针对回收代,比如年轻代,通过 -XX:newSize -XX:MaxNewSize来设置其绝对大小。同

 

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

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

相关文章

项目代码版本控制与保护

一、版本命名规则 1.1 需求开发分支命名规则 格式&#xff1a;dev_v版本号_需求名称 案例&#xff1a;dev_v01.31_TX202301141 dev_v01.31_数字产品平台订单查询优化 1.2 测试环境发布分支命名规则 格式&#xff1a;uat_deploy 1.3 预上环境分支命名规则 格式&#xff1a…

Apsara Clouder云计算专项技能认证:云服务器ECS入门

文章目录 一、什么是云服务器ECS二、如何获取一台云服务器ECS三、5分钟学会管理云服务器ECS四、【实验】ECS云服务器新手上路五、【实验】基于ECS搭建云上博客入口: https://edu.aliyun.com/certification/cldc15 一、什么是云服务器ECS

8K超高清企业获广东省专精特新称号,背后意味着什么?

2023年开年之际&#xff0c;广州博冠光电科技股份有限公司及其子公司广州博冠智能科技有限公司通过“2022年广东省专精特新中小企业”和“2022年广东省名优高新技术产品”2项权威认定荣誉。广东省“专精特新”认定是什么&#xff1f;“专精特新”是指中小企业具备专业化、精细化…

github入门指南

文章目录什么是开源开源项目托管平台github使用介绍什么是github使用github&#xff08;1&#xff09;注册&#xff08;2&#xff09;创建远程仓库&#xff08;3&#xff09;github功能介绍本地创建仓库与远程仓库进行关联什么是开源 开源&#xff0c;即开放源代码&#xff0c…

呆头鹅批量剪辑视频软件:玩的就是裂变,剪辑需求者的终身助理

文案来自于公众号&#xff1a;生财风暴 图片来自于公众号&#xff1a;生财风暴 随着5G时代到来&#xff0c;短视频&#xff0c;电商&#xff0c;自媒体等平台全面崛起&#xff0c;目前影视制作行业前景的9字真言: 需求大&#xff01;门槛低&#xff01;薪资高&#xff01; 呆头…

迟来的年度总结 —— 我与 csdn 的故事

&#x1f451;作者主页&#xff1a;安 度 因 &#x1f3e0;学习社区&#xff1a;StackFrame &#x1f4d6;专栏链接&#xff1a;我的故事 文章目录引子我的大一我与 csdn 的相识第一次创作喜悦 —— 干劲满满挫败 —— 厚积薄发意外 —— 收获果实博客新星陪伴社区与StackFrame…

修改 bootargs 方式增加分区(mtd分区和blkdevparts分区)

1、Linux内核设置分区的两种方式 1.1、内核代码中写死 在内核的平台代码中写死&#xff0c;然后在初始化NandFlash的时候设置。 1.2、uboot通过bootargs传递分区表 (1)u-boot将分区信息&#xff08;形如&#xff1a;mtdpartsxxx&#xff0c;blkdevpartsxxx&#xff09;添加到b…

​力扣解法汇总2325. 解密消息

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a;力扣 描述&#xff1a; 给你字符串 key 和 message &#xff0c;分别表示一个加密密钥和一段加密消息。…

FastDeploy全场景高性能AI部署工具:加速打通AI模型产业落地的最后一公里

飞桨目前为开发者提供了涵盖多种领域的模型套件&#xff0c;开发者可以使用这些套件基于自身数据集快速完成深度学习模型的训练。但在实际产业部署环境下&#xff0c;开发者在部署模型到不同硬件和不同场景时面临以下三个痛点问题。 碎片化。在部署过程中&#xff0c;由于深度学…

投资中的不可能三角

投资中避不开这个不可能三角&#xff01; 而这“三个角”指的是高收益、低风险、高流动性&#xff0c;“不可能三角”则指的是“三角”难以同时满足。 因为这三角的关系是相互制衡的&#xff01; ①若要追求较低风险条件下的较高收益&#xff0c;需要以时间换取空间&#xff0…

认识现有的频谱监测方案

摘要随着频谱监测行业的不断发展&#xff0c;频谱仪已经不仅局限于频谱监测与获取&#xff0c;随着5G的逐渐普及&#xff0c;在垂直领域的各种应用中都有其价值。针对不同的应用场景与行业&#xff0c;现有的市场上主要存在三种频谱监测方案&#xff0c;我们将在本文中进行总结…

【AI-NLP】Transformer浅记

文章目录提出背景总体结构EncoderDecoder细节再探源码阅读主函数部分主模型定义参考资料提出背景 改进了RNN的训练慢的缺点&#xff0c;利用self-attention机制实现并行计算。并且Transformer可以增加到非常深的深度&#xff0c;充分发掘DNN模型的特性&#xff0c;提升模型准确…

2022 IoTDB Summit:中冶赛迪工业互联网平台与CISDigital-TimeS(基于IoTDB)在钢铁行业的实践...

12 月 3 日、4日&#xff0c;2022 Apache IoTDB 物联网生态大会在线上圆满落幕。大会上发布 Apache IoTDB 的分布式 1.0 版本&#xff0c;并分享 Apache IoTDB 实现的数据管理技术与物联网场景实践案例&#xff0c;深入探讨了 Apache IoTDB 与物联网企业如何共建活跃生态&#…

即时通讯开发之详解TCP/IP中的DNS 域名系统

前面已经提到了访问一台机器要靠 IP 地址和 MAC 地址,其中,MAC 地址可以通过 ARP 协议得到,所以这对用户是透明的,但是 IP 地址就不行,无论如何用户都需要用一个指定的 IP 来访问一台计算机,而 IP 地址又非常不好记,于是就出现了 DNS 系统。DNS 系统介绍DNS 的全称是 Domain Na…

MYSQL作业

MySQL 作业一、填空题 1、交叉连接查询返回的结果是被连接的两张数据表中所有数据行的_笛卡尔积___ 。2、左连接查询的结果包括 LEFT JOIN 子句中左表的_所有记录___&#xff0c;以及右表中满足连接条件的记录。3、内连接查询的语法中&#xff0c;ON 用于指定查询的__匹配条件_…

从0开始学python -16

Python3 列表-1 序列是 Python 中最基本的数据结构。 序列中的每个值都有对应的位置值&#xff0c;称之为索引&#xff0c;第一个索引是 0&#xff0c;第二个索引是 1&#xff0c;依此类推。 Python 有 6 个序列的内置类型&#xff0c;但最常见的是列表和元组。 列表都可以…

六西格玛,为质量人的职业发展保驾护航

常常会有做质量的人来问优思学院&#xff1a;在质量界混&#xff0c;应学什么好&#xff1f; 大部分时候&#xff0c;我都会提议他們&#xff1a;有空就要学六西格玛吧&#xff01; 他会回应&#xff1a;六西格玛感觉太难懂了&#xff0c;学了也没什么用啊&#xff0c;哪有那…

tensorflow框架之会话、张量、变量OP

2.3 会话、张量、变量OP 学习目标 目标 应用sess.run或者eval运行图程序并获取张量值应用feed_dict机制实现运行时填充数据应用placeholder实现创建占位符知道常见的TensorFlow创建张量知道常见的张量数学运算操作说明numpy的数组和张量相同性说明张量的两种形状改变特点应用…

前言技术之OAuth2.0

1、什么是OAuth2.0 OAuth2.0是目前使用非常广泛的授权机制&#xff0c;用于授权第三方应用获取用户的数据。 举例说明&#xff1a; 用户可以通过选择其他登录方式来使用gitee&#xff0c;这里就使用到了第三方认证。 OAuth 引入了一个授权层&#xff0c;用来分离两种不同的角…

记录--手把手教学,实现一个优雅的图片预览

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 在我们开发项目中&#xff0c;经常会遇到预览图片的需求。也就是点击图片&#xff0c;会全屏显示该图片。需求很简单&#xff0c;但是如何让实现更优雅就需要花点心思了。 最终效果图 基础版本 实现方…