多线程数据库驱动加载引发的惨案

简介: 以往项目生产java.lang.OutOfMemoryError: Compressed class space错误排查总结,

背景:

以往数据类项目,有这样的一个业务场景:异构数据源整库和各库表业务数据量定时统计,时效性每天统计,数据源涉及200+数据源,表数据量较大,客户要求每天凌晨精确查询数据量,不允许通过数据元数据模糊查询表数据量,项目上利用了多线程并发定时统计汇聚,因涉及到数据源多样性,采用动态加载不同版本的数据库驱动实现jdbc连接,导致的java.lang.OutOfMemoryError: Compressed class space错误,服务不可用

排查思路:

1、了解jdk8 压缩空间是什么?

jdk8内存结构

   jdk8之前有perm这一整块内存来存klass等信息(参数里必不可少地会配置-XX:PermSize以及-XX:MaxPermSize来控制这块内存的大小),jdk8后改成了metaspace元数据存储空间

为啥需要压缩空间?

OOPS:原始对象指针(ordinary object pointers)

    64bit的JVM出现后,OOPS的尺寸也变成了64bit,比之前的大了一倍。这会引入性能损耗——占的内存double了,并且同尺寸的CPU Cache要少存一倍的OOPS

    使用了这个压缩功能,每个对象中的 Klass* 字段就会被压缩成 32bit(不是所有的 oop 都会被压缩的),总所周知 Klass* 指向的 Klass 在永久代(Java7 及之前)。但是在 Java8 及之后,永久代没了,有了一个 Metaspace,于是之前压缩指针 Klass* 指向的这块 Klass 区域有了一个名字 —— Compressed Class Space。Compressed Class Space 是 Metaspace 的一部分,默认大小为 1G。所以其实 Compressed Class Space 这个名字取得很误导,压缩的并不是 Klass,而是 Klass*

metaspace元数据存储空间组成?

  • Klass Metaspace
  • NoKlass Metaspace

Klass Metaspace就是用来存klass的,klass是我们熟知的class文件在jvm里的运行时数据结构,不过有点要提的是我们看到的类似A.class其实是存在heap里的,是java.lang.Class的一个对象实例。这块内存是紧接着Heap的,和我们之前的perm一样,这块内存大小可通过-XX:CompressedClassSpaceSize参数来控制,这个参数前面提到了默认是1G,但是这块内存也可以没有,假如没有开启压缩指针就不会有这块内存,这种情况下klass都会存在NoKlass Metaspace里,另外如果我们把-Xmx设置大于32G的话,其实也是没有这块内存的,因为会这么大内存会关闭压缩指针开关。还有就是这块内存最多只会存在一块。

NoKlass Metaspace专门来存klass相关的其他的内容,比如method,constantPool等,这块内存是由多块内存组合起来的,所以可以认为是不连续的内存块组成的。这块内存是必须的,虽然叫做NoKlass Metaspace,但是也其实可以存klass的内容,上面已经提到了对应场景。

Klass Metaspace和NoKlass Mestaspace都是所有classloader共享的,所以类加载器们要分配内存,但是每个类加载器都有一个SpaceManager,来管理属于这个类加载的内存小块。如果Klass Metaspace用完了,那就会OOM了,不过一般情况下不会,NoKlass Mestaspace是由一块块内存慢慢组合起来的,在没有达到限制条件的情况下,会不断加长这条链,让它可以持续工作。


2、jstat监控服务器内存以及gc情况

jstat-gc${pid} 10001000-gc:将显示与垃圾收集相关的统计信息${pid}:目标JVM进程ID1000:每10,00毫秒(即1秒)将打印一次统计信息。1000:将打印1000次迭代的统计信息S0C:第一个幸存区的大小S1C:第二个幸存区的大小S0U:第一个幸存区的使用大小S1U:第二个幸存区的使用大小EC:伊甸园区的大小EU:伊甸园区的使用大小OC:老年代大小OU:老年代使用大小MC:方法区大小MU:方法区使用大小CCSC:压缩类空间大小CCSU:压缩类空间使用大小YGC:年轻代垃圾回收次数YGCT:年轻代垃圾回收消耗时间FGC:老年代垃圾回收次数FGCT:老年代垃圾回收消耗时间GCT:垃圾回收消耗总时间



jstat命令样例:

在启动各个数据源物理表统计数据量的任务后,发现压缩空间巨增到700多M

程序代码并发流程如下:

排查出现主动加载类的代码段如下:


3、优化方案,全局缓存加载过的驱动


相关文章
|
3月前
|
Oracle 关系型数据库 Java
实时计算 Flink版操作报错之读取Oracle数据库时遇到找不到驱动,是什么原因
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
实时计算 Flink版操作报错之读取Oracle数据库时遇到找不到驱动,是什么原因
|
3月前
|
人工智能 数据管理 Serverless
阿里云数据库走向Serverless与AI驱动的一站式数据平台具有重大意义和潜力
阿里云数据库走向Serverless与AI驱动的一站式数据平台具有重大意义和潜力
434 2
|
26天前
|
SQL druid Java
线程池相关故障问题之Druid数据库连接池中,为何需要设置TransactionTimeout
线程池相关故障问题之Druid数据库连接池中,为何需要设置TransactionTimeout
|
6天前
|
SQL Java 数据库连接
java连接数据库加载驱动到java项目
该博客文章介绍了如何在Java项目中通过代码加载数据库驱动并连接SQL Server数据库,包括具体的加载驱动和建立数据库连接的步骤,以及如何将驱动包添加到Java项目的构建路径中。
|
6天前
|
数据库连接 数据库
实现加载驱动、得到数据库对象、关闭资源的代码复用,将代码提取到相应的工具包里边。优化程序
该博客文章展示了如何通过创建工具类`Connectiontools`实现数据库连接、语句执行以及资源关闭的代码复用,以优化程序并提高数据库操作的效率和安全性。
|
30天前
|
人工智能 自然语言处理 数据管理
数据平台演进问题之自然语言处理技术在AI驱动的数据库中的作用是什么
数据平台演进问题之自然语言处理技术在AI驱动的数据库中的作用是什么
|
3月前
|
人工智能 数据管理 大数据
阿里云数据库走向Serverless与AI驱动的一站式数据平台是一个很有前景和意义的发展方向
阿里云数据库走向Serverless与AI驱动的一站式数据平台是一个很有前景和意义的发展方向
84 2
|
30天前
|
机器学习/深度学习 人工智能 自然语言处理
数据平台演进问题之数据的资产怎么被AI驱动的数据库理解
数据平台演进问题之数据的资产怎么被AI驱动的数据库理解
|
2月前
|
SQL 存储 NoSQL
SQL与NoSQL数据库的选择:技术与场景驱动下的决策
【6月更文挑战第16天】**SQL vs NoSQL数据库:技术与应用场景比较。SQL数据库以其关系模型、ACID特性、灵活查询及事务处理见长,适合结构化数据和强一致性场景。NoSQL则以数据模型灵活性、高可扩展性、高性能及低成本著称,适合大数据、高并发和快速迭代的需求。选择应基于业务需求、数据特性、系统架构和成本。**
|
2月前
|
存储 缓存 Linux
深入理解Linux中的`db_load`命令:数据库加载的利器
`db_load`是Linux下处理Berkeley DB的关键命令,用于将文本数据加载到数据库中。它支持多种文本格式,如键值对和CSV,并具有灵活的选项,如指定数据库类型、缓存大小、日志记录和错误处理。通过`-f`加载文本文件,`-s`设定数据库类型,`-l`设置缓存。本文详细介绍了`db_load`的使用方法和高级特性,并给出案例,如将CSV用户信息加载到Btree数据库。了解并善用`db_load`能提升数据处理效率和安全性。