- 浏览: 427125 次
- 性别:
- 来自: 深圳
文章分类
最新评论
-
Glogo:
楼主您好,我在试验您的代码的时候发现新开的三个子线程并没有一直 ...
java 高并发 ReentrantLock -- 可重入的锁 -
univasity:
最近发觉也被限速了,投诉一下就好一会~~ 看来明天又要和电信M ...
ADSL上网速度慢 都是帐号限速惹的祸 -
liuyuanhui0301:
java 高并发 ReentrantLock -- 可重入的锁 -
dang_java:
呵.很好的说明文档.
JXTA技术与应用发展 -
helloqidi:
谢谢,学习了
SQL中exists和in的区别
线程运行栈信息的获取
一、问题的引入
我们在Java程序中使用日志功能(JDK Log或者Log4J)的时候,会发现Log系统会自动帮我们打印出丰富的信息,格式一般如下:
[运行时间] [当前类名] [方法名]
INFO: [用户信息]
具体例子如Tomcat启动信息:
Jul 9, 2004 11:22:41 AM org.apache.coyote.http11.Http11Protocol start
INFO: Starting Coyote HTTP/1.1 on port 8080
看起来这毫无神奇之处,不就是打印了一条信息吗?但如果好奇心重一点,追寻后面的实现原理,会发现这确实很神奇。
上面的Log信息的[当前类名] [方法名]部分 不是用户自己添加的,而是Log系统自动添加的。这意味着Log系统能够自动判断当前执行语句是哪个类的哪个方法。这是如何做到的?
我们翻遍java.lang.reflection package,幻想着找到一个Statement语句级别的Reflection类,通过这个Statement对象获得Method,然后通过这个Method获得declared Class。这不就获得对应的Class和Method信息了吗?这是一个不错的构想,但也只能是一个构想;因为没有这个Statement对象。
再想一下。对了,Java不是有一个Thread类吗?Thread.currentThread()方法获取当前线程,我们能不能通过这个当前线程获取当前运行的Method和Class呢?很遗憾,如果你还在用JDK1.4或以下版本,那么找不到这样的方法。(JDK1.5的情况后面会讲)
再想一下。对了,我们都有很深刻的印象,当系统抛出Exception的时候,总是打印出一串的信息,告诉我们Exception发生的位置,和一层一层的调用关系。我们也可以自己调用Exception的printStackTrace()方法来打印这些信息。这不就是当前线程运行栈的信息吗?找到了,就是它。
Exception的printStackTrace()方法继承自Throwable,那么我们来看一下,JDK的Throwable的printStackTrace()方法是如何实现的。
我们先来看JDK1.3的源代码,会发现Throwable.printStackTrace()方法调用了一个native printStackTrace0()方法。我们找不到任何线索,可以用在我们自己的Java代码中。
那怎么办?Throwable.printStackTrace()的输出结果字符串里面不是包含了当前线程运行栈的所有信息吗?我们可以从这个字符串中抽取自己需要的信息。JDK1.3的时代,也只能这么做了。
二、Log4J 1.2的相关实现
Log4J 1.2是JDK1.3时代的作品。我们来看相关源代码。
- /**
- Instantiate location information based on a Throwable. We
- expect the Throwable <code>t</code>, to be in the format
- <pre>
- java.lang.Throwable
- ...
- at org.apache.log4j.PatternLayout.format(PatternLayout.java:413);
- at org.apache.log4j.FileAppender.doAppend(FileAppender.java:183);
- at org.apache.log4j.Category.callAppenders(Category.java:131);
- at org.apache.log4j.Category.log(Category.java:512);
- at callers.fully.qualified.className.methodName(FileName.java:74);
- ...
- </pre>
- */
- public LocationInfo(Throwable t, String fqnOfCallingClass); {
- String s;
- …
- t.printStackTrace(pw);;
- s = sw.toString();;
- sw.getBuffer();.setLength(0);;
- …. // 这里的代码省略
- }
/** Instantiate location information based on a Throwable. We expect the Throwable <code>t</code>, to be in the format <pre> java.lang.Throwable ... at org.apache.log4j.PatternLayout.format(PatternLayout.java:413); at org.apache.log4j.FileAppender.doAppend(FileAppender.java:183); at org.apache.log4j.Category.callAppenders(Category.java:131); at org.apache.log4j.Category.log(Category.java:512); at callers.fully.qualified.className.methodName(FileName.java:74); ... </pre> */ public LocationInfo(Throwable t, String fqnOfCallingClass); { String s; … t.printStackTrace(pw);; s = sw.toString();; sw.getBuffer();.setLength(0);; …. // 这里的代码省略 }
这里我们可以看到整体的实现思路。
首先,t.printStackTrace(pw); 获得stack trace字符串。这个t是 new Throwable()的结果。用户程序调用Log4J方法之后,Log4J自己又进行了4次调用,然后才获得了 t = new Throwable() :
at org.apache.log4j.PatternLayout.format(PatternLayout.java:413)
at org.apache.log4j.FileAppender.doAppend(FileAppender.java:183)
at org.apache.log4j.Category.callAppenders(Category.java:131)
at org.apache.log4j.Category.log(Category.java:512)
那么,往下走4行,就可以回到用户程序本身的调用信息:
at callers.fully.qualified.className.methodName(FileName.java:74)
这一行里面,类名、方法名、文件名、行号等信息全有了。解析这一行,就可以获得需要的所有信息。
三、JDK1.4 Log的相关实现
Log4J大获成功,Sun决定在JDK1.4中引入这个Log功能。
为了免去解析StackTrace字符串的麻烦,JDK1.4引入了一个新的类,StackTraceElement。
public final class StackTraceElement implements java.io.Serializable {
// Normally initialized by VM (public constructor added in 1.5)
private String declaringClass;
private String methodName;
private String fileName;
private int lineNumber;
可以看到,恰好包括类名、方法名、文件名、行号等信息。
我们来看JDK1.4 Log的相关实现。
LocationInfo.java 的infoCaller方法(推算调用者)
// Private method to infer the caller's class and method names
private void inferCaller() {
…
// Get the stack trace.
StackTraceElement stack[] = (new Throwable()).getStackTrace();
// First, search back to a method in the Logger class.
…. // 这里的代码省略
// Now search for the first frame before the "Logger" class.
while (ix < stack.length) {
StackTraceElement frame = stack[ix];
String cname = frame.getClassName();
if (!cname.equals("java.util.logging.Logger"))
// We've found the relevant frame.
… // 这里的代码省略
}
// We haven't found a suitable frame, so just punt. This is
// OK as we are only committed to making a "best effort" here.
}
从注释中就可以看出实现思路。过程和Log4J异曲同工。只是免去了解析字符串的麻烦。
四、Log4J 1.3 alpha的相关实现
既然JDK1.4中引入了StackTraceElement类,Log4J也要与时俱进。LocationInfo类也有了相应的变化。
/**
Instantiate location information based on a Throwable. We
expect the Throwable <code>t</code>, to be in the format
<pre>
java.lang.Throwable
...
at org.apache.log4j.PatternLayout.format(PatternLayout.java:413)
at org.apache.log4j.FileAppender.doAppend(FileAppender.java:183)
at org.apache.log4j.Category.callAppenders(Category.java:131)
at org.apache.log4j.Category.log(Category.java:512)
at callers.fully.qualified.className.methodName(FileName.java:74)
...
</pre>
<p>However, we can also deal with JIT compilers that "lose" the
location information, especially between the parentheses.
*/
public LocationInfo(Throwable t, String fqnOfInvokingClass) {
if(PlatformInfo.hasStackTraceElement()) {
StackTraceElementExtractor.extract(this, t, fqnOfInvokingClass);
} else {
LegacyExtractor.extract(this, t, fqnOfInvokingClass);
}
}
可以看到,Log4J首先判断Java平台是否支持StackTraceElement,如果是,那么用StackTraceElementExtractor,否则使用原来的LegacyExtractor。
下面来看StackTraceElementExtractor.java
/**
* A faster extractor based on StackTraceElements introduced in JDK 1.4.
*
* The present code uses reflection. Thus, it should compile on all platforms.
*
* @author Martin Schulz
* @author Ceki G&lc&
*
*/
public class StackTraceElementExtractor {
protected static boolean haveStackTraceElement = false;
private static Method getStackTrace = null;
private static Method getClassName = null;
private static Method getFileName = null;
private static Method getMethodName = null;
private static Method getLineNumber = null;
…. // 以下代码省略
可以看到,Log4J 1.3仍然兼容JDK1.3,而且为JDK1.4也做了相应的优化。
五、JDK1.5的Thread Stack Trace
JDK1.5在Thread类里面引入了getStackTrace()和getAllStackTraces()两个方法。这下子,我们不用 (new Throwable()).getStackTrace ();可以调用
Thread.getCurrentThread().getStackTrace()来获得当前线程的运行栈信息。不仅如此,只要权限允许,还可以获得其它线程的运行栈信息。
/**
* Returns an array of stack trace elements representing the stack dump
* of this thread. This method will return a zero-length array if
* this thread has not started or has terminated.
* If the returned array is of non-zero length then the first element of
* the array represents the top of the stack, which is the most recent
* method invocation in the sequence. The last element of the array
* represents the bottom of the stack, which is the least recent method
* invocation in the sequence.
*
* <p>If there is a security manager, and this thread is not
* the current thread, then the security manager's
* <tt>checkPermission</tt> method is called with a
* <tt>RuntimePermission("getStackTrace")</tt> permission
* to see if it's ok to get the stack trace.
*
* <p>Some virtual machines may, under some circumstances, omit one
* or more stack frames from the stack trace. In the extreme case,
* a virtual machine that has no stack trace information concerning
* this thread is permitted to return a zero-length array from this
* method.
*
* @return an array of <tt>StackTraceElement</tt>,
* each represents one stack frame.
*
* @since 1.5
*/
public StackTraceElement[] getStackTrace() {
if (this != Thread.currentThread()) {
// check for getStackTrace permission
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkPermission(
SecurityConstants.GET_STACK_TRACE_PERMISSION);
}
}
if (!isAlive()) {
return EMPTY_STACK_TRACE;
}
Thread[] threads = new Thread[1];
threads[0] = this;
StackTraceElement[][] result = dumpThreads(threads);
return result[0];
}
/**
* Returns a map of stack traces for all live threads.
*
* @since 1.5
*/
public static Map<Thread, StackTraceElement[]> getAllStackTraces() {
// check for getStackTrace permission
// Get a snapshot of the list of all threads
}
六、总结
从总的发展趋势来看,JDK不仅提供越来越多、越来越强的功能,而且暴露给用户的控制方法越来越多,越来越强大。
package test;
public class Main {
public static void main(String[] args) {
new Test().test();
}
}
package test;
import java.util.Map;
import sun.reflect.Reflection;
public class Test {
public void test() {
System.out.println("------------Reflection.getCallerClass(i)------------");
System.out.println("Reflection.getCallerClass(0)=" + Reflection.getCallerClass(0));
System.out.println("Reflection.getCallerClass(1)=" + Reflection.getCallerClass(1));
System.out.println("Reflection.getCallerClass(2)=" + Reflection.getCallerClass(2));
System.out.println("Reflection.getCallerClass(3)=" + Reflection.getCallerClass(3));
System.out.println("Reflection.getCallerClass(4)=" + Reflection.getCallerClass(4));
System.out.println("\n------------new Throwable().getStackTrace()------------");
StackTraceElement[] elements = new Throwable().getStackTrace();
for (int i = 0; i < elements.length; i++) {
System.out.println(elements[i]);
}
System.out.println("\n------------Thread.currentThread().getStackTrace()------------");
elements = Thread.currentThread().getStackTrace();
for (int i = 0; i < elements.length; i++) {
System.out.println(elements[i]);
}
System.out.println("\n------------Thread.getAllStackTraces()------------");
Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
elements = map.get(Thread.currentThread());
for (int i = 0; i < elements.length; i++) {
System.out.println(elements[i]);
}
}
}
运行结果:
------------Reflection.getCallerClass(i)------------
Reflection.getCallerClass(0)=class sun.reflect.Reflection
Reflection.getCallerClass(1)=class test.Test
Reflection.getCallerClass(2)=class test.Main
Reflection.getCallerClass(3)=null
Reflection.getCallerClass(4)=null
------------new Throwable().getStackTrace()------------
test.Test.test(Test.java:33)
test.Main.main(Main.java:5)
------------Thread.currentThread().getStackTrace()------------
java.lang.Thread.getStackTrace(Thread.java:1426)
test.Test.test(Test.java:39)
test.Main.main(Main.java:5)
------------Thread.getAllStackTraces()------------
java.lang.Thread.dumpThreads(Native Method)
java.lang.Thread.getAllStackTraces(Thread.java:1477)
test.Test.test(Test.java:45)
test.Main.main(Main.java:5)
发表评论
-
深入JVM锁机制
2011-09-19 01:00 971目前在Java中存在两种 ... -
java 正则表达式 非捕获组(特殊构造)Special Constructs(Non-Capturing)
2011-06-20 23:15 1640针对Java API文档中的正则表达式关于特殊构造(非捕获组) ... -
Java文件映射[mmap]揭秘
2011-06-08 20:10 1169前言 相信现在 ... -
原创 java的Mmap二三事
2011-06-08 19:46 1195转自 :http://blog.csdn.net/kabini ... -
java 安全沙箱模型详解
2011-04-18 16:29 936起到第一道安全保障作 ... -
非阻塞算法-ReentrantLock代码剖析之ReentrantLock.lock
2011-04-15 13:59 1078ReentrantLock是java.util.concurr ... -
CyclicBarrier与CountDownLatch、栅栏与计数器
2011-04-15 10:39 1460在多线程设计中,我猜常常会遇到线程间相互等待以及某个线程等待1 ... -
Java KeyStore
2011-04-13 17:17 1463简介Java自带的keytool工具是个密钥和证书管理工具。它 ... -
Security Managers and the JavaTM 2 SDK
2011-04-12 13:37 771The original Link : http://do ... -
Something about SecurityManager
2011-04-12 13:33 779The Java Security was made up o ... -
Using the Java SecurityManager with Tomcat
2011-04-12 13:30 992Why use a SecurityManager? The ... -
Java安全管理器(Security Manager)(
2011-04-11 14:54 881转载自: http://blog.sina.com.cn/s/ ... -
Java对象的强、软、弱和虚引用(1)
2011-04-01 08:44 795本文介绍Java对象的强 ... -
Java对象的强引用、软引用、弱引用和虚引用
2011-04-01 08:39 902在JDK1.2以前的版本中, ... -
java 高并发 ReentrantLock -- 可重入的锁
2011-03-30 08:09 2364ReentrantLock -- 可重入的锁 可重入锁指 ... -
java序列化——Serializable
2011-03-15 23:17 1068类通过实现 java.io.Serializable 接口 ... -
Java aio(异步网络IO)初探
2011-03-11 16:34 1560按照《Unix网络编程》的 ... -
JAVA NIO 简介
2011-03-11 13:38 11121. 基本 概念 IO 是主存和外部设备 ( 硬盘、终 ... -
[字节码系列]ObjectWeb ASM构建Method Monitor
2011-03-08 18:08 894在前面的篇章中,我们看到Java Instru ... -
深入了解Java ClassLoader、Bytecode 、ASM、cglib
2011-03-08 16:35 855一、Java ClassLoader 1,什 ...
相关推荐
Linux利用进程栈和线程栈来管理这些信息,并通过内核栈来处理系统调用和中断处理。 总的来说,栈作为一种内存管理机制,在Linux系统中扮演着基础而重要的角色。它不仅支撑了程序内部的函数调用过程,更是操作系统多...
线程堆栈信息可以让我们深入观察到线程的运行状况,包括线程的当前状态、调用堆栈、锁信息等。为了获取这些信息,我们可以使用jstack工具,它是Java开发工具包(JDK)的一部分,能够输出Java进程的线程堆栈跟踪信息...
在多线程环境中,我们通常需要获取每个线程的栈信息来帮助分析问题,例如查看函数调用堆栈、查找变量值或者理解程序执行流程。 pstack-1.3.1这个压缩包文件很可能包含了名为`pstack`的开源工具源代码。`pstack`是一...
栈主要用于保存线程的运行状态和局部自动变量。栈的特点包括: - 栈在创建线程时由系统自动初始化。 - 每个线程的栈空间相互独立,这保证了线程安全(Thread Safe)。 - 栈中的数据(如局部变量和函数参数)会在函数...
3. **进程和线程信息查询**:这通常涉及`OpenProcess`和`OpenThread`函数来获取进程或线程句柄,然后使用`GetProcessTimes`和`GetThreadContext`等函数获取详细信息,如CPU使用率、内存占用、运行时间等。...
通过分析和运行这个程序,你可以更深入地理解伪线程和独立栈的工作原理,以及如何在C语言中实现并发执行。 总结来说,伪线程和独立栈是编程中实现并发的一种方式,尤其在资源有限或者需要避开操作系统限制的场景下...
系统线程信息包括线程ID、优先级、创建时间、状态(如运行、等待、休眠等)、栈信息、CPU占用率等。获取这些信息对于调试、性能分析和系统监控至关重要。 3. **取系统线程信息**:易语言中的这个功能模块,允许...
线程是操作系统中的基本执行单元,它在一个进程中独立运行,拥有自己的执行上下文,包括程序计数器、寄存器和栈。一个进程可以包含多个线程,它们共享同一地址空间,但拥有各自的执行路径。查看进程中的线程可以帮助...
- **线程**:线程是程序执行的最小单元,每个线程有自己的执行序列,可以独立于其他线程运行。 - **进程与线程**:一个进程中可以有多个线程,它们共享进程的资源,但各自拥有独立的运行上下文,如程序计数器、栈...
上述代码首先获取所有线程的ID,然后对每个ID调用`getThreadInfo()`方法获取详细的线程信息,包括线程ID、名称、状态、栈深度和锁定的监视器数量。这将帮助我们理解每个线程当前在做什么。 除了直接使用`...
线程调度是操作系统决定哪个线程应该获得CPU执行权的过程。Windows采用了抢占式多任务调度策略,这意味着具有更高优先级的线程可以中断当前执行的线程,从而获取CPU时间。Windows提供了多种线程优先级级别,从IDLE_...
线程互斥对象是一种同步机制,当一个线程获得了Mutex的所有权后,其他试图获取该Mutex的线程将被阻塞,直到拥有Mutex的线程释放它。在易语言中,可以使用相关函数或者对象来创建和管理Mutex,确保在多线程环境中,...
- 获取当前线程栈溢出保护区域的大小。 **设置范围** - 设置线程的执行范围。 **获取范围** - 查询线程的执行范围。 **设置线程并行级别** - 调整线程并行执行的程度。 **获取线程并行级别** - 查询当前线程...
### 线程及多线程的详细讲解 ...- 线程运行完毕或被终止后,进入死亡状态。 线程的状态转换体现了多线程编程中的复杂性和灵活性,正确地理解和管理这些状态对于开发高性能、健壮的多线程应用至关重要。
2. **线程信息获取**:获取特定线程的详细信息,如创建时间、已运行时间、当前CPU占用率等。 3. **线程调度分析**:分析线程的调度行为,帮助理解系统资源分配和性能瓶颈。 4. **线程同步监视**:监控线程间的同步...
- `pthread_attr_getstack`函数用于获取线程栈的地址和大小。 #### 五、用同步对象编程 ##### 互斥锁属性 - 互斥锁是一种常用的同步机制,用于保护共享资源免受多个线程的同时访问。 ##### 初始化互斥锁属性对象...
3. **获取线程上下文**:`GetThreadContext` 用于获取线程的寄存器状态,特别是 ESP 和 EIP 寄存器,它们分别表示栈指针和指令指针,对于控制线程执行至关重要。 4. **插入异常函数**:在栈中插入异常函数 `...
脚本可以访问包括线程堆栈在内的各种运行时信息,例如通过 `com.sun.btrace.probes.ThreadProbe` 探针来获取当前线程的堆栈轨迹。一旦编写好脚本,bTrace会将其注入到运行中的Java应用,然后收集和打印出有关线程...
1. 定义线程函数:线程函数是线程运行的入口点,每个线程都有自己的线程函数。 2. 创建线程:使用系统提供的API函数创建线程,例如在Windows API中使用`CreateThread`函数,而在POSIX标准中使用`pthread_create`函数...