Pitest内存泄露分析 (工具使用IDEA、Jprofiler)

目录

一、环境

二、概述

2.1变异测试整体流程

2.2内存溢出原因

主进程(设计问题)

子进程(CoverageMinion)

三、过程分析

3.1调试环境搭建

主/子进程远程调试

Jprofiler远程调试

3.2子进程内存分析

内存泄漏确定

占主要堆空间的对象

占主要空间对象补充。。

分析MockClassLoader被引用情况

监控ClassLoader

子线程未结束

子线程被加入shutdownHooks

MbeanServerFactory

3.3主进程内存分析

四、小结


 

 

一、环境

Pitest版本: 1.6.4

Github fork: https://github.com/YKRY35/pitest

Jprofiler工具: 11.1.5

 

Pitest以maven插件的方式运行。

公司使用中,单元测试使用的powermock框架,过程中发生OOM,机器内存32G。

 

二、概述

2.1变异测试整体流程

 

可以看到,主进程为了替换字节码,需要javaagent,所以会起子进程来跑覆盖率和变异。

覆盖率阶段,所有的任务都在单个子进程跑,而变异阶段,会拆分任务,到不同的子进程去跑。所以存在内存泄漏和设计问题的情况下,覆盖率阶段相比变异阶段更容易Out Of Memory。

这里分析和解决内存问题,是针对主进程的覆盖率阶段和子进程1 CoverageMinion子进程。

 

 

2.2内存溢出原因

先下结论。

  • 主进程(设计问题)

主进程是设计问题,导致内存过大直到溢出。

注意这个数据结构:

https://github.com/YKRY35/pitest/blob/master/pitest-entry/src/main/java/org/pitest/coverage/execute/Receive.java#L24

 

private final Map<Long, BlockLocation>    probeToBlock  = new ConcurrentHashMap<>();

 

这个数据结构是子进程内存溢出的根源。

两个原因导致这个数据结构过大:

1. MockClassLoader

PowerMock为了环境隔离,自定义了类加载器,并且重写loadClass方法,于是不遵守父委派模型,所以同一个类在不同单侧脚本中会被重复加载。

即:

 

可以看到同一个UserService类,因为类加载器不同,且不遵守父委派,所以它的Class对象会反复被加载,经过transformer被插入探针。

子进程的探针是要汇报到主进程作记录的,因此probeToBlock这个数据结构会特别大。

同一个类会被反复注入探针,反复次数取决于跑到这个类的单测数。

在单测1跑完,跑单测2时,Class<UserService> @1这里头的探针就没有用了,可以释放。

 

2. 无用探针注入

Pitest在支持powermock时,考虑到powermock使用javassist来加载最初的class文件,可能是希望在powermock修改class之前先拿到最初的class文件,因此pitest修改了javassist的字节码。

见:

https://github.com/YKRY35/pitest/blob/master/pitest/src/main/java/org/pitest/mutationtest/mocksupport/JavassistInputStreamInterceptorAdapater.java#L64

 

@Override

  public void visitMethodInsn(final int opcode, final String owner,

    final String name, final String desc, boolean itf) {

  if ((opcode == Opcodes.INVOKEINTERFACE)

      && owner.equals("javassist/ClassPath") && name.equals("openClassfile")) {

    this.mv.visitMethodInsn(Opcodes.INVOKESTATIC, this.interceptorClass, name,

        "(Ljava/lang/Object;Ljava/lang/String;)Ljava/io/InputStream;", false);

  } else {

    this.mv.visitMethodInsn(opcode, owner, name, desc, itf);

  }

  

}

它将javassist的ClassPath/openClassfile方法调用全部替换为自己框架内的JavassistCoverageInterceptor/openClssfile,所以在mock之前pitest就能拿到没被修改过的字节码文件了。

但是!!!注意:

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值