出现时间 : 9月9号,晚上发布后出现
项目: dzs-**
背景: 下午开始接入Skywalking. 晚上发布后出现该问题.
错误堆栈
java.lang.ClassCircularityError: java/lang/CharacterData00
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 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() {
@Override
public 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.”)), 项目重跑发现没有问题。