某天上服务器看了下gc情况,发现状况不对,启动了才2天的服务器发生了360次fullgc,这个频率肯定高了

说明

S0C、S1C、S0U、S1U:Survivor 0/1区容量(Capacity)和使用量(Used)
EC、EU:Eden区容量和使用量
OC、OU:年老代容量和使用量
PC、PU:永久代容量和使用量
YGC、YGT:年轻代GC次数和GC耗时
FGC、FGCT:Full GC次数和Full GC耗时
GCT:GC总耗时<br><br>

jmap下载下内存看下

jmap -dump:format=b,file=文件名 [pid]

用eclipse的mat工具打开,很明显的就能发现NonRegisteringDriver里的ConcurrentHashMap占用了大量内存

去NonRegisteringDriver的源码看看,发现该类只有一个ConcurrentHashMap,也就是connectionPhantomRefs,里面存放了ConnectionImpl的虚引用

继续往下找,发现在这个位置会放入该map

看下调用链路,猜测是获取数据库链接的时候丢了个jdbcConnection的虚引用进这个map

继续找找看看这个存放了虚引用的map在哪会被使用,发现 AbandonedConnectionCleanupThread里起了个线程去遍历虚引用的队列,在虚引用被回收后会进入这个队列,在这边获取后进行链接的cleanup操作

至此已经清楚ConnectionPhantomReference的作用就是在gc回收之后,能让代码从队列里获取实例再进行相关资源的回收

那么问题来了:

1:为什么会造成那么频繁的fullgc

2:connectionPhantomRefs这个map的作用是什么

1.1:java的堆内存对象的ygc次数过多后将会进入老年代

一、对象何时进入老年代
(1)当对象首次创建时, 会放在新生代的eden区, 若没有GC的介入,会一直在eden区, GC后,是可能进入survivor区或者年老代 (2)当对象年龄达到一定的大小 ,就会离开年轻代, 进入老年代。 而对象的年龄是由GC的次数决定的 -XX:MaxTenuringThreshold=n  新生代的对象最多经历n次GC, 就能晋升到老年代, 但不是必要条件    -XX:TargetSurvivorRatio=n  用于设置Survivor区的目标使用率,即当survivor区GC后使用率超过这个值, 就可能会使用较小的年龄作为晋升年龄 (3)除年龄外, 对象体积也会影响对象的晋升的, 若对象体积太大, 新生代无法容纳这个对象 -XX:PretenureSizeThreshold  即对象的大小大于此值, 就会绕过新生代, 直接在老年代分配, 此参数只对串行回收器以及ParNew回收有效, 而对ParallelGC回收器无效

1.2:代码使用的为HikariCP连接池,而HikariCP有几个参数

idleTimeout (连接空闲超时时间,默认 10 分钟)
maxLifetime(连接最大生存时间,默认 30 分钟)
maxPoolSize (连接池最大连接数)
minIdle(最小空闲连接数,默认等于 maxPoolSize)

可以看出当数据库连接空闲时间超过了 idleTimeout,那么将会关闭,直到数量为 minIdle。还有当数据库连接存活时间达到了 maxLifetime ,那么连接也会关闭,然后再创建新的连接,而每次创建新的连接都将重复上述步骤

1.3:而我的系统默认配置了500个连接数(minIdle=maxPoolSize=500),而查看阿里云的监控可以得知活跃的连接数基本就十几个,导致jdbc连接疯狂的在关闭打开,而在30分钟之内经过多次ygc之后,ConnectionImpl都进入了老年代,而老年代的gc间隔很长,就导致connectionPhantomRefs里堆积了一大堆虚引用的对象,而触发一次gc后会释放

2:为了验证这个map的作用,我建了个类

public class Test  {
public static ReferenceQueue<Object> refQueue = new ReferenceQueue<Object>();
public static Map<PhantomReference, PhantomReference> map = new HashMap<>();
public PhantomReference<Object> phanRef; public void test() throws InterruptedException {
Object obj = new Object(); phanRef = new PhantomReference<Object>(obj, refQueue); // map.put(phanRef, phanRef); }
}

然后在另外一个类里面执行以下方法

 public static void main(String[] args) throws InterruptedException {
Test test = new Test();
test.test();
//PhantomReference<Object> s = drugTest.phanRef;
//System.out.println(s);
test = null;
System.gc();
System.gc();
System.gc();
System.out.println(test.refQueue.poll());
//System.out.println(s);
//s = null;
System.out.println(test.refQueue.poll());
}

当map.put(phanRef, phanRef);这行代码被注释时,输出

null
null

当map.put(phanRef, phanRef);这行代码存在时,输出

null
java.lang.ref.PhantomReference@4d591d15

当map.put(phanRef, phanRef);这行代码存在时,并且取消PhantomReference<Object> s = drugTest.phanRef; s = null;的注释时输出

null
java.lang.ref.PhantomReference@4d591d15

从这个实验可以看出,当phanRef没有被其他地方引用时,当他所属的实例类被回收后他并不会进入到弱引用的队列中(可能和gc机制有关吧)

解决方案:

1:调整HikariCP连接池的参数

2:调整jvm堆内存的参数

3:手动删除connectionPhantomRefs这个ConcurrentHashMap中的数据

4:手动调system.gc(我觉得这个方法很扯淡,手动掉还不如让jvm自己判断调呢,反正不会内存溢出)

附上调整后的参数

https://www.cnblogs.com/MRLL/p/12721295.html

参考文章

https://www.jianshu.com/p/6d37afd1f072

https://www.cnblogs.com/newcj/archive/2011/05/15/2046882.html

https://www.cnblogs.com/yaowen/p/10975241.html

https://blog.csdn.net/u010657094/article/details/104042326/

NonRegisteringDriver造成的内存频繁FullGc的更多相关文章

  1. 记一次内存无法回收导致频繁fullgc机器假死的思路

    确定挂机 络绎不绝的来不同类型的bug 当bug滚滚而来时,不要怀疑,你的发布的应用基本是不可用状态了.观察哨兵监控数据,特别是内存打到80%基本就挂机了,或者监控数据缺失也基本是挂机了.此时应当马上 ...

  2. 关于GC(上):Apache的POI组件导致线上频繁FullGC问题排查及处理全过程

    某线上应用在进行查询结果导出Excel时,大概率出现持续的FullGC.解决这个问题时,记录了一下整个的流程,也可以作为一般性的FullGC问题排查指导. 1. 生成dump文件 为了定位FullGC ...

  3. 一次性搞清楚线上CPU100%,频繁FullGC排查套路

    “ 处理过线上问题的同学基本上都会遇到系统突然运行缓慢,CPU 100%,以及 Full GC 次数过多的问题. 当然,这些问题最终导致的直观现象就是系统运行缓慢,并且有大量的报警. 本文主要针对系统 ...

  4. 一次压测中tomcat生成session释放不及时导致的频繁fullgc性能优化案例

    性能问题:老年代一直处于占满状态,为什么没有发生内存溢出 以HotSpot VM的分代式GC为例,普通对象分配都是在young gen进行的,具体是从在位于young gen中的eden space中 ...

  5. 服务器CPU很高,频繁FullGC排查小总结

    可以分为如下步骤: ①通过 top 命令查看 CPU 情况,如果 CPU 比较高,则通过 top -Hp 命令查看当前进程的各个线程运行情况. 找出 CPU 过高的线程之后,将其线程 id 转换为十六 ...

  6. 记一次线上频繁fullGc的排查解决过程

    发生背景 最近上线的一个项目几乎全是查询业务,并且都是大表的慢查询,sql优化是做了一轮又一轮,前几天用户反馈页面加载过慢还时不时的会timeout,但是我们把对应的sql都优化一遍过后,前台响应还是 ...

  7. JDBC驱动自身问题引发的FullGC

    公众号HelloJava刊出一篇<MySQL Statement cancellation timer 故障排查分享>,作者的某服务的线上机器报 502(502是 nginx 做后端健康检 ...

  8. Java堆内存

    Java 中的堆是 JVM 所管理的最大的一块内存空间,主要用于存放各种类的实例对象. 在 Java 中,堆被划分成两个不同的区域:新生代 ( Young ).老年代 ( Old ).新生代 ( Yo ...

  9. jvm对大对象分配内存的特殊处理(转)

    前段日子在和leader交流技术的时候,偶然听到jvm在分配内存空间给大对象时,如果young区空间不足会直接在old区切一块过去.对于这个结论很好奇,也比较怀疑,所以就上网搜了下,发现还真有这么回事 ...

随机推荐

  1. Canvas方法总结

    渲染上下文 getContext() // 获得渲染上下文和它的绘画功能 绘制形状 绘制矩形 fillRect(x, y, width, height) // 绘制一个填充的矩形 strokeRect ...

  2. iviewadmin url 加入 Router base #viewDesignAdmin

    router/index.js const router = new Router({   routes,   base: '/viewDesignAdmin/',   mode: 'history' ...

  3. .net 4.0 以下HttpWebRequest Header 修改hosts方法

    .net 4.0 以下HttpWebRequest Header 修改hosts方法 特此记录 public class CusteredHeaderCollection : WebHeaderCol ...

  4. Asp.Net Core 3.1 获取不到Post、Put请求的内容 System.NotSupportedException Specified method is not supported

    # 问题 是这样的,我.net core 2.1的项目,读取.获取Post请求内容的一段代码,大概这样: [HttpPost] public async Task<IActionResult& ...

  5. Java 访问修饰符大全详解

    鉴于笔试面试总会遇到,决心仔细认真梳理一下: 1:涉及的关键字:public,default(表示缺省),protected,private,static,final,abstract. 2:关键字含 ...

  6. 向Hive中传入变量的方式

    Hive向程序中传递变量的方式 暴力替换 字符串替换 正则替换 模板引擎 系统环境变量 shell环境变量:${env:varname} system系统变量:${system:varname} hi ...

  7. Hive数据倾斜的原因及主要解决方法

    数据倾斜产生的原因 数据倾斜的原因很大部分是join倾斜和聚合倾斜两大类 Hive倾斜之group by聚合倾斜 原因: 分组的维度过少,每个维度的值过多,导致处理某值的reduce耗时很久: 对一些 ...

  8. Mysql获取系统时间,年,月,日

      Mysql数据库中获取系统时间,年,月,日单个获取 获取当前系统日期时间:select SYSDATE() AS 系统日期时间; 获取当前系统年月日:select current_date AS ...

  9. Building Applications with Force.com and VisualForce(Dev401)( 八):Designing Applications for Multiple Users: Controling Access to Records.

    Module Objectives1.List feature that affect access to data at the record level.2.List the organizati ...

  10. 码云上开源JAVA项目收藏

    一. 个人学习项目 1. BootDo面向学习型的开源框架 (可以当做 管理台脚手架) BootDo是高效率,低封装,面向学习型,面向微服的开源Java EE开发框架. BootDo是在SpringB ...