Java的Groovy执行器内存泄露(MetaSpace)问题分析与解决办法

news2024/11/25 0:43:31

环境与背景

在java程序中通过GroovyScriptEvaluator执行器创建脚本Script对象调用Groovy脚本语言来完成某些功能, ,会通过AppClassLoader或者GroovyClassLoader去生产一个随机的名称的Groovy的Script类对象,导致元数据,产生的class类会被AppClassLoader或者GroovyClassLoader内部对应的Map所引用,导致不不能满足被垃圾回收的条件, 在执行脚本期间,得到了 Out of Metaspace出错

jdk8, groovy 2.4.6版本

  <dependency>
     <groupId>org.codehaus.groovy</groupId>
      <artifactId>groovy-all</artifactId>
      <version>2.4.6</version>
  </dependency>

执行脚本方式

1 Spring GroovyScriptEvaluator执行器
2.ScriptEngineManager 基于JDK的SPI方式提供的执行脚本引擎
3.基于GroovyShell

问题复现方式

GroovyScriptEngineImpl se;

while (true)
{
    se = new GroovyScriptEngineImpl(new GroovyClassLoader());
    CompiledScript script = se.compile("println(\"hello\")");
    script.eval(se.createBindings());
}

或者

    private static GroovyScriptEvaluator evaluator = new GroovyScriptEvaluator();


    public static Object evaluateScript(String script, Map<String, Object> context) {
        ScriptSource scriptSource = new StaticScriptSource(script);
        return evaluator.evaluate(scriptSource, context);
    }

    public static void main(String[] args) {
        while (true) {
            evaluateScript("1+2", Maps.newHashMap());
        }
    }
   
JVM调优复现参数

-Xms200m -Xmx200m -Xss1m -XX:MetaspaceSize=40m -XX:MaxMetaspaceSize=40m -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution -XX:+PrintHeapAtGC -XX:+PrintGCApplicationStoppedTime -Xloggc:C:\Users\admin\Desktop\GC\gc-%t.log -XX:+TraceClassLoading -XX:+TraceClassUnloading -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=50M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=C:\Users\admin\Desktop\GC\memory.log

类加载机制-双亲委派

Groovy的ClassLoader体系:

Bootstrap ClassLoader

sun.misc.Launcher.ExtClassLoader // 即Extension ClassLoader

sun.misc.Launcher.AppClassLoader // 即System ClassLoader

org.codehaus.groovy.tools.RootLoader // 以下为User Custom ClassLoader

groovy.lang.GroovyClassLoader

groovy.lang.GroovyClassLoader.InnerLoader

异常现象

groovy 2.4.6 版本
在这里插入图片描述
在这里插入图片描述

解决思路: 1. 更换版本groovy2.4.8以及以上版本,则会回收Script类,可以解决问题,但是会存在性能问题
在这里插入图片描述
解决思路2: 升级jdk版本, 升级jdk版本11, 尝试解决类不会回收的问题

groovy 2.4.6 版本 出现问题
Exception in thread "main" java.lang.OutOfMemoryError: Metaspace

排查Metaspace方式

1. 通过jps命令,查看java进程pid

![在这里插入图片描述](https://img-blog.csdnimg.cn/447af6724a804de4b00fcd6f11afcb90.png

2. 通过jinfo 命令查看额外的java参数配置信息

在这里插入图片描述

3. 通过jmap用来查看内存信息,实例个数以及占用内存大小

在这里插入图片描述

  • num:序号
  • instances:实例数量
  • bytes:占用空间大小
  • class name:类名称,[C is a char[],[S is a short[],[I is a int[],[B is a byte[],[[I is a int[][]
4. 通过jstat 命令查看堆内存各部分的使用量,以及加载类的数量

评估程序内存使用及GC压力整体情况
在这里插入图片描述

S0C:第一个幸存区的大小,单位KB
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
OC:老年代大小
OU:老年代使用大小
MC:方法区大小(元空间)
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间,单位s
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间,单位s
GCT:垃圾回收消耗总时间,单位s

堆内存统计

在这里插入图片描述

NGCMN:新生代最小容量
NGCMX:新生代最大容量
NGC:当前新生代容量
S0C:第一个幸存区大小
S1C:第二个幸存区的大小
EC:伊甸园区的大小
OGCMN:老年代最小容量
OGCMX:老年代最大容量
OGC:当前老年代大小
OC:当前老年代大小
MCMN:最小元数据容量
MCMX:最大元数据容量
MC:当前元数据空间大小
CCSMN:最小压缩类空间大小
CCSMX:最大压缩类空间大小
CCSC:当前压缩类空间大小
YGC:年轻代gc次数
FGC:老年代GC次数

jconsole 查看堆栈元数据等信息

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

分析内存溢出信息 jvisualvm

-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=./ (路径)
通过参数, 导入dump文件
在这里插入图片描述
在这里插入图片描述

  1. 查看相应的堆栈信息, 以及方法入栈/出栈等信息, 系统属性 显示线程 等确定代码异常位置
  2. 查看类信息, 找出类创建数量多的类, 是否有自己写的相关类信息,进行代码定位
代码解决方案
  1. 升级groovy版本为2.4.8或以上版本
  • 1. java SPI提供的接口方式
@Slf4j
public class GroovyScriptUtil {

    public GroovyScriptUtil() {
    }

    private static final String EngineName = "groovy";
    private static ScriptEngine engine = null;
    static{
        ScriptEngineManager factory = new ScriptEngineManager();
        engine = factory.getEngineByName(EngineName);
    }
    public static Object evaluateScript(String script, Map<String, Object> params) {
        try {
            Bindings bindings = engine.createBindings();
            bindings.putAll(params);
            return engine.eval(script,bindings);
        } catch (Exception e) {
            log.error("script脚本执行异常,script:{},params:{}",script,params);
            log.error("script脚本执行异常:",e);
            return null;
        }
    }
}
  • 2.groovy shell方式
public class GroovyUtils {
    private static GroovyShell groovyShell = new GroovyShell();

    private static Map<String, Script> scriptCache = new ConcurrentHashMap<>();

    private static <T> T invoke(String scriptText, String function, Object... objects) throws Exception {
        Script script;
        String cacheKey = DigestUtils.md5Hex(scriptText);

        if (scriptCache.containsKey(cacheKey)) {
            script = scriptCache.get(cacheKey);
        } else {
            script = groovyShell.parse(scriptText);
            scriptCache.put(cacheKey, script);
        }
        
        return (T) InvokerHelper.invokeMethod(script, function, objects);
    }
}

常用的JVM参数

-XX:+HeapDumpOnOutOfMemoryError:表示当JVM发生OOM时,自动生成DUMP文件。

-XX:HeapDumpPath:表示生成DUMP文件的路径,也可以指定文件名称。

-Xms	启动应用时,JVM 堆空间的初始大小值。
-Xmx	应用运行中,JVM 堆空间的极限值。为了不消耗扩大JVM堆空间分配的开销,将此参数和-Xms这个两个值设为相等,考虑到需要开线程,将此值设置为总内存的80%.
-Xss    单个线程堆栈大小值;JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K
-Xms 的默认值是物理内存的 1/64 但小于1G,-Xmx 的默认值是物理内存的 1/4 但小于1G
-Xmn	此参数硬性规定堆空间的新生代空间大小,推荐设为堆空间大小的1/4。
-XX:+UseParNewGC 开启此参数,多个CPU可并发进行垃圾回收,可提高垃圾回收的速度。此参数和+UseParallelGC,-XX:ParallelGCThreads搭配使用。
+UseParallelGC  年轻代使用并发收集,而年老代仍旧使用串行收集 。可提高系统的吞吐量。
XX:ParallelGCThreads  年轻代并行垃圾收集的前提下(对并发也有效果)的线程数,增加并行度.此值最好配置与处理器数目相等
-Xloggc:<file>    将 GC 状态记录在文件中 (带时间戳)
JVM 参数关系到系统的性能,而其中 -XX:PermSize,-XX:MaxPermSize,-Xms,-Xmx 和 -Xmn 这 5 个参数更是直接关系到系统的性能,系统是否会出现内存溢出。
-XX:PermSize 和 -XX:MaxPermSize 分别设置应用服务器启动时,永久存储区的初始大小和极限大小;在生产环境中强烈推荐将这个两个值设置为相同的值,以避免分配永久存储区的开销,如果不进行设置-XX:MaxPermSize ,则默认值为 64M (jdk1.8之前使用)
-XX:MaxMetaspaceSize: 设置元空间最大值, 默认是-1, 即不限制, 或者说只受限于本地内存大小。
-XX:MetaspaceSize: 指定元空间触发Fullgc的初始阈值(元空间无固定初始大小), 以字节为单位,默认是21M,达到该值就会触发
full gc进行类型卸载, 同时收集器会对该值进行调整: 如果释放了大量的空间, 就适当降低该值; 如果释放了很少的空间, 那么在不超
过-XX:MaxMetaspaceSize(如果设置了的话) 的情况下, 适当提高该值。这个跟早期jdk版本的-XX:PermSize参数意思不一样,-
XX:PermSize代表永久代的初始容量。


-Xloggc:/path/logs/gc-%t.log	GC日志存储的文件路径,%t 表示每次启动时用时间戳命名日志文件,如 gc-2021-03-29_20-41-47.log。
-XX:+UseGCLogFileRotation	开启日志文件分割
-XX:NumberOfGCLogFiles=16	最多分割几个文件,超过之后从头开始写
-XX:GCLogFileSize=100M	每个文件存储的上限大小,超过就触发分割
-XX:+PrintGCDetails	打印GC详细信息
-XX:+PrintGCDateStamps	打印GC日期戳
-XX:+PrintTenuringDistribution	打印对象年龄分布日志,分析 GC 时的晋升情况和晋升导致的高暂停
-XX:+PrintHeapAtGC	GC 后打印堆数据,用于对比一下 GC 前后的堆内存情况,更直观
-XX:+PrintReferenceGC	打印 Reference 处理信息
-XX:+PrintGCApplicationStoppedTime	打印 STW 暂停时间
java -XX:+PrintCommandLineFlags -version   查看JVM使用什么垃圾收集器
 -XX:+HeapDumpOnOutOfMemoryError
 -XX:HeapDumpPath=./ (路径)
jps    查看java进程号
jinfo
jstat
jstack
jamp
jconsole.exe
jvisualvm.exe

调优工具Arthas

支持 JDK6+, 采用命令行交互模式,可以方便的定位和诊断
线上程序运行问题。Arthas 官方文档十分详细,详见:https://alibaba.github.io/arthas
1.用java -jar运行即可,可以识别机器上所有Java进程
2. 选择识别的java进程号, 输入即可进入arthas 的控制台
3. 输入dashboard,可以查看整个进程的运行情况,线程、内存、GC、运行环境信息
4. 输入thread可以查看线程详细情况
5. 输入 jad加类的全名 可以反编译,这样可以方便我们查看线上代码是否是正确的版本
6. 使用 ognl 命令可以查看线上系统变量的值,甚至可以修改变量的值
7. 还有很多命令可以在官网查看,进行使用即可

参考

https://www.cnblogs.com/hanease/p/16131868.html
https://blog.csdn.net/ld851/article/details/111180179
https://blog.csdn.net/weixin_46017166/article/details/127820176
jvm: https://api.dandelioncloud.cn/article/details/1497067909796171778

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

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

相关文章

软件测试必备知识

一、软件测试的基本概念①需求IEEE规定&#xff1a;软件需求是&#xff08;1&#xff09;用户解决问题或达到目标所需 条件或权能。&#xff08;2&#xff09;系统或系统部件要满足合同、标准、规范或其他正式规定文档所需具有的条件或权能。一种反应上面&#xff08;1&#xf…

MyBatisPlus 批量添加

文章目录现状优化效果现状 一般来说&#xff0c;批量插入可以使用 MyBatisPlus 中 ServiceImpl 自带的方法 saveBatch 打开 sql 日志&#xff0c;application.yml 添加配置&#xff0c;mapper-locations 配置 mapper 路径 mybatis-plus:configuration:log-impl: org.apache.i…

LEAP模型的能源环境发展、碳排放建模预测及不确定性分析

LEAP&#xff08;Long Range Energy Alternatives Planning System/ Low emission analysis platform&#xff0c;长期能源可替代规划模型&#xff09;是一种自下而上的能源-环境核算工具&#xff0c;由斯德哥尔摩环境研究所和美国波士顿大学联合研发。该模型与情景分析法紧密结…

基于Jetson Tx2 Nx的Qt、树莓派等ARM64架构的Ptorch及torchvision的安装

前提 已经安装好了python、pip及最基本的依赖库 若未安装好点击python及pip安装请参考这篇博文 https://blog.csdn.net/m0_51683386/article/details/129320492?spm1001.2014.3001.5502 特别提醒 一定要先根据自己板子情况&#xff0c;找好python、torch、torchvision的安…

信箱|邮箱系统

技术&#xff1a;Java、JSP等摘要&#xff1a;在经济全球化和信息技术飞速发展的今天&#xff0c;通过邮件收发进行信息传递已经成为主流。目前&#xff0c;基于B/S&#xff08;Browser/Server&#xff09;模式的MIS&#xff08;Management information system&#xff09;日益…

C语言数组二维数组

C 语言支持数组数据结构&#xff0c;它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据&#xff0c;但它往往被认为是一系列相同类型的变量。 数组的声明并不是声明一个个单独的变量&#xff0c;比如 runoob0、runoob1、…、runoob99&#xff0c;而是…

唤醒手腕前端 Electron Gui 桌面应用开发详细教程(流程模型、进程通信、进程沙盒化)

流程模型 Electron 继承了来自 Chromium 的多进程架构&#xff0c;这使得此框架在架构上非常相似于一个现代的网页浏览器。 为什么不是一个单一的进程&#xff1f; 网页浏览器是个极其复杂的应用程序。 除了显示网页内容的主要能力之外&#xff0c;他们还有许多次要的职责&a…

红黑树(Insert())

文章目录红黑树代码红黑树性质红黑树vsAVL树红黑树的实现Insert()情况一&#xff1a;如果我插入的新节点时红色的情况二&#xff1a;叔叔是黑色或者不存在情况三: cur红,p为红,g为黑,u不存在或者为黑-双旋检查erase()红黑树vsAVL树红黑树的应用&#xff1a;红黑树 二叉搜索树 …

分布式框架RabbitMQ详解以及编程特性Base实现

消息队列MQ MQ的主要作用有&#xff1a; 异步 例子: 快递员发快递&#xff0c;直接到客户家效率会很低。引入菜鸟驿站后&#xff0c;快递员只需要把快递放到菜鸟驿站&#xff0c;就可以继续发其他快递去了。客户再按自己的时间安排去菜鸟驿站取快递. 作用:异步能提高系统的…

基于明道云平台重建医院管理流程

一、龙华区医疗信息化建设情况 首先&#xff0c;给大家介绍一下龙华区医疗信息化建设的情况&#xff0c;龙华区位于深圳市的中部&#xff0c;目前下属3家公立医院&#xff0c;2家公共卫生机构。2017年&#xff0c;龙华区提出了建设智慧龙华总体框架方案&#xff0c;龙华区卫生…

FPGA Cameralink图像生成模板,Cameralink采集卡图像采集

FPGA Cameralink图像生成模板&#xff0c;Cameralink采集卡图像采集。1&#xff1a;采集卡安装2&#xff1a;FPGA产生图像模板module vga_lcd_driver(input clk,input [7:0] r_i,input [7:0] g_i,input [7:0] b_i,output [7:0] r_o,output [7:0] g_o,output [7:0] b_o,output d…

《QDebug 2023年2月》

一、Qt Widgets 问题交流 二、Qt Quick 问题交流 三、其他 1.使用 QDir::toNativeSeparators() 转换路径中的 "/" 分割符为 "\" 在网上抄代码的时候&#xff0c;总会遇到这样的代码&#xff1a; file_path.replace("/", "\\"); …

界面开发(3)--- PyQt5用户登录界面连接数据库

文章目录数据库账户注册账号登录找回密码为了实现用户登录界面的登录功能&#xff0c;我们必须建立一个数据库&#xff0c;并把账号和对应的密码&#xff0c;存储到数据库中。如果输入的账号和密码与数据库中的一致&#xff0c;那我们就允许用户登录&#xff0c;进入新的界面。…

win10 WSL2 使用Ubuntu配置与安装教程

Win10 22H2ubuntu 22.04ROS2 文章目录一、什么是WSL2二、Win10 系统配置2.1 更新Windows版本2.2 Win10系统启用两个功能2.3 Win10开启BIOS/CPU开启虚拟化(VT)&#xff08;很关键&#xff09;2.4 下载并安装wsl_update_x64.msi2.5 PowerShell安装组件三、PowerShell安装Ubuntu3.…

WMS相关知识点(二)

目录一、Android 图形显示系统1. 从下层往上层理解一、Android 图形显示系统 1. 从下层往上层理解 1.1 显示屏 显示屏上的内容&#xff0c;是从硬件帧缓冲区的&#xff0c;大致读取过程为&#xff1a;从Buffer的起始地址开始&#xff0c;从上往下&#xff0c;从左往右&#x…

Redis持久化RDB和AOF

Redis 是内存数据库&#xff0c;数据都是存储在内存中&#xff0c;为了避免进程退出导致数据的永久丢失&#xff0c;需要定期将 Redis 中的数据以某种形式&#xff08;数据或命令&#xff09;从内存保存到硬盘。当下次 Redis 重启时&#xff0c;利用持久化文件实现数据恢复。除…

《C++ Primer》 第十二章 动态内存

《C Primer》 第十二章 动态内存 动态内存与智能指针 shared_ptr允许多个指针指向同一个对象&#xff1b;unique_ptr则“独占”所指向的对象&#xff0c;weak_ptr指向shared_ptr所管理的对象。这三种类型都定义在memory头文件中。 shared_ptr类&#xff1a;默认初始化的智能…

Docker容器化部署.net core API

1.为API集成Docker环境。&#xff08;VS自带&#xff0c;傻瓜式操作&#xff09; 1.1 点击项目&#xff0c;右键&#xff0c;添加&#xff0c;选择Docker支持 1.2 找到项目根目录中的Dockerfile文件&#xff0c;这是VS刚刚帮我们自动生成的。进入和做如图标红地方修改。 把文…

map和set 的封装

文章目录引入key-value模型map和set底层setset的几个重要接口mapmap几个重要的接口map和set的封装引入 对于map和set的引入&#xff0c;我们用一道在程序中常见的问题解决&#xff1a; 给定一个数组int arr[]{1,2,1,3,1,4,1,5,5,2,3,4,5};&#xff0c;给出以下问题的解决方案&…

【基础算法】双指针---最长连续不重复子序列

&#x1f339;作者:云小逸 &#x1f4dd;个人主页:云小逸的主页 &#x1f4dd;Github:云小逸的Github &#x1f91f;motto:要敢于一个人默默的面对自己&#xff0c;强大自己才是核心。不要等到什么都没有了&#xff0c;才下定决心去做。种一颗树&#xff0c;最好的时间是十年前…