出现时间 : 9月9号,晚上发布后出现
项目: dzs-**
背景: 下午开始接入Skywalking. 晚上发布后出现该问题.
错误堆栈
java.lang.ClassCircularityError: java/lang/CharacterData00at java.lang.CharacterData.of(CharacterData.java:82)at java.lang.Character.toLowerCase(Character.java:6338)at java.lang.Character.toLowerCase(Character.java:6309)at java.lang.String.toLowerCase(String.java:2581)at java.lang.String.toLowerCase(String.java:2670)at com.raycloud.safe.SecurityManager.checkPermission(SecurityManager.java:235)at sun.misc.URLClassPath.check(URLClassPath.java:642)at sun.misc.URLClassPath$JarLoader.checkResource(URLClassPath.java:961)at sun.misc.URLClassPath$JarLoader.getResource(URLClassPath.java:1044)at sun.misc.URLClassPath.getResource(URLClassPath.java:239)at sun.misc.URLClassPath.getResource(URLClassPath.java:292)at java.lang.ClassLoader.getBootstrapResource(ClassLoader.java:1264)at java.lang.ClassLoader.getResource(ClassLoader.java:1093)at java.net.URLClassLoader.getResourceAsStream(URLClassLoader.java:232)at org.apache.skywalking.apm.dependencies.net.bytebuddy.dynamic.ClassFileLocator$ForClassLoader.locate(ClassFileLocator.java:438)....at org.apache.skywalking.apm.dependencies.net.bytebuddy.matcher.ElementMatcher$Junction$Disjunction.matches(ElementMatcher.java:160)at org.apache.skywalking.apm.dependencies.net.bytebuddy.matcher.ElementMatcher$Junction$Disjunction.matches(ElementMatcher.java:160)at org.apache.skywalking.apm.dependencies.net.bytebuddy.matcher.ElementMatcher$Junction$Disjunction.matches(ElementMatcher.java:160)at org.apache.skywalking.apm.agent.core.plugin.match.ProtectiveShieldMatcher.matches(ProtectiveShieldMatcher.java:46)at org.apache.skywalking.apm.dependencies.net.bytebuddy.agent.builder.AgentBuilder$RawMatcher$ForElementMatchers.matches(AgentBuilder.java:1299)at org.apache.skywalking.apm.dependencies.net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.doTransform(AgentBuilder.java:10304)at org.apache.skywalking.apm.dependencies.net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:10263)at org.apache.skywalking.apm.dependencies.net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.access$1600(AgentBuilder.java:10029)at org.apache.skywalking.apm.dependencies.net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$LegacyVmDispatcher.run(AgentBuilder.java:10648)at org.apache.skywalking.apm.dependencies.net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$LegacyVmDispatcher.run(AgentBuilder.java:10595)at java.security.AccessController.doPrivileged(Native Method)at org.apache.skywalking.apm.dependencies.net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:10186)at sun.instrument.TransformerManager.transform(TransformerManager.java:188)at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:428)at java.lang.CharacterData.of(CharacterData.java:82)at java.lang.Character.toLowerCase(Character.java:6338)at java.lang.Character.toLowerCase(Character.java:6309)at java.lang.String.toLowerCase(String.java:2581)at java.lang.String.toLowerCase(String.java:2670)at com.raycloud.safe.SecurityManager.checkPermission(SecurityManager.java:235)at java.lang.SecurityManager.checkPropertyAccess(SecurityManager.java:1294)at java.lang.System.getProperty(System.java:717)at com.raycloud.agent.raycloud.task.VpcHostReplaceTask$2.run(VpcHostReplaceTask.java:61)at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308)at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180)at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)at java.lang.Thread.run(Thread.java:748)
错误定位
移除 skywalking 的的 agent 监控后,恢复正常. 猜测问题出现在 双agent 上,网上出现的该错误较少,典型链接有
大致分析原因是一个 class 被重复加载了2次,具体哪里未知 , 可以推测的是 agent 执行前加载一个class,然后这个class又进入了 agent 里边进行加载.
本地复现
根据线上稳定的复现方式,下载了 skywalking 的agent配置和 公司内部的agent ,使用一个普通的 SpringBoot 开始复现.
java -XX:-UseSplitVerifier -noverify -Ddns.config=k8s -Duser.home=/data/project/chenshun -javaagent:/data/project/chenshun/project-agent-1.0.0-SNAPSHOT.jar -Dskywalking.logging.dir=/data/project/chenshun/logs/skywalking -Dskywalking.agent.service_name=chenshun -javaagent:/data/project/chenshun/skywalking-agent/skywalking-agent.jar -Dskywalking_config=/data/project/chenshun/skywalking-agent/config/agent.config -jar test_web.jar
分别引入了 project-agent-1.0.0-SNAPSHOT.jar 和 /data/project/chenshun/skywalking-agent/skywalking-agent.jar , 在10分钟的时候准时出现该错误,10-30秒后进程直接退出. 在线上的表现就是正常发布后, 10分钟开始重启.
经过对比堆栈发现,10分钟开始重启的原因是 agent 内部存在一个 10分钟 的定时任务, 且从堆栈可以看出问题出现在这里。
at com.raycloud.agent.raycloud.task.VpcHostReplaceTask$2.run(VpcHostReplaceTask.java:61)
该定时任务是被一个线程池执行的,在多次测试后发现,使用 ScheduledExecutorService 就有问题
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {@Overridepublic Thread newThread(@Nonnull Runnable r) {Thread thread = new Thread(r);thread.setName("dns-陈顺");thread.setDaemon(true);return thread;}});
使用 Timer 和 new Thread() 就没有问题. 然后使用如下方式去执行
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor()
也没有问题,最后将问题聚焦于 ThreadFactory .
这个 ThreadFactory 是一个普通的,可以被抄袭的 ThreadFactory 实现,然后拷贝 newSingleThreadScheduledExecutor#ThreadFactory 的 默认实现也没有问题,最后排查下来发现将名字设置为中文该问题必然出现,设置为英文+数字就不会出现这个问题。 根据这个结果先把agent重新打包发布上去了,使用 taobaoapi-invoke-notify 项目测试确实没有问题。
最终原因
根据线程名字和堆栈( at com.raycloud.safe.SecurityManager.checkPermission(SecurityManager.java:235))去定位哪里会去使用线程的名字,定位到 safe 会使用这个名字去加快项目启动
问题出现,然后将这部分注释,发现线程名字设置为中文也没有问题. 根据堆栈反向推导
- 项目启动时加载定时任务的时候由于agent没有初始化完毕,所以第一次跑不会进入safe没问题。
- 10分钟后,agent已经加载完成,每一个加载的
class文件都会被transform,所以后续跑的任务进入了agent的加载- 公司的agent是采用的白名单形式,如果一个类不需要加强那么跳过.
- skywalking采用的是判断的形式,需要进行规则判断,所以这部分堆栈很长

- 从这里可以判断应该是
skywalking经过判断后需要去加载文件 又进入了safe模块进行权限判断. 由此引发的2次加载问题
因此只要将 java/lang/CharacterData00 在 skywalking 的zgent中进行跳过即可. 在skywalking的SkyWalkingAgent中加入 .or(ElementMatchers.nameStartsWith(“java.lang.”)), 项目重跑发现没有问题。
