Bug
今天配solrcloud,1 shard,3 replicator。运行起来后,其中1个实例的Thread Dump报错,提示Invalid thread ID parameter: 0。
hack了一下solr源代码,在ThreadDumpHandler里面添加了一些log。
ThreadMXBean tmbean = ManagementFactory.getThreadMXBean();
long[] tids = tmbean.getAllThreadIds();
log.info("all threads tids:" + (tids == null? "null" :tids.length));
if (tids != null) {
for (long l :tids) {
log.info(String.valueOf(l));
}
log.info("all thread infos:");
NamedList<SimpleOrderedMap<Object>> lst = new NamedList<SimpleOrderedMap<Object>>();
for (long l :tids) {
if (l > 0) {
ThreadInfo ti = tmbean.getThreadInfo(l, Integer.MAX_VALUE);
if (ti != null) {
log.info(ti.toString());
lst.add( "thread", getThreadInfo( ti, tmbean ) );
}
} else {
log.info("invalid thread id:" + l);
}
}
system.add( "threadDump", lst );
}
发现id为0的线程是Thread[QuorumPeer:/0.0.0.0:9983,5,main],zookeeper的一个线程。
ThreadInfo
ThreadInfo不仅包含Thread的一些信息,还包含一些额外的信息,比如线程状态、线程阻塞原因、线程调用堆栈等。
如果只是想知道Thread的id,name,classloader,通过下面这种方式就可以得到
List<Field> fields = new ArrayList<Field>();
try {
Field f = Thread.class.getDeclaredField("contextClassLoader");
fields.add(f);
f.setAccessible(true);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
try {
Field f = Thread.class.getDeclaredField("tid");
fields.add(f);
f.setAccessible(true);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
for (Thread thread : Thread.getAllStackTraces().keySet()) {
for (Field f : fields) {
try {
System.out.println(f.getType().getName() + ":" + f.getName() + "="
+ f.get(thread));
} catch (IllegalAccessException e) {
}
}
System.out.println();
}
然而分别用这两种方式,得到的结果却不一样,这个线程Thread[QuorumPeer:/0.0.0.0:9983,5,main]通过第一种方式得到的线程id是0,而第二种方式却是27,是不是很诡异。线程id为0时,通过 sun.management.ThreadImpl.getThreadInfo(0) 会抛出一个IllegalArgumentException "Invalid thread ID parameter: xxx"
看jdk的代码,发现这两种方式得到所有Thread实例的方法sun.management.ThreadImpl.getThreads() 和 java.lang.Thread.getThreads() 都是native的。于是搞出jdk c的源码看了一下,这两个方法在c中底层实际是调用的一个方法,所以是没有区别的。都是调用的jvm.h中声明的下面这个方法。
ThreadImpl.getThreadInfo也是一个native方法调用,依赖于ThreadImpl.getThreads(),所以问题就是为什么C底层实现是一样的,得到的Thread Id不一样呢。。//TODO 仔细check jdk c源代码
JNIEXPORT jobjectArray JNICALL
JVM_GetAllThreads(JNIEnv *env, jclass dummy);
解决
最后还是在stackoverflow上面得到了一个满意的答案,感谢国际友人,他发现这是jdk的一个bug 6404306,其实这个bug参考6412693,6412693还是一个open状态。。大意是说可以看到部分初始化的JNI attached threads