地球人都知道C++里有一个typeid操作符可以用来获取一个类型/表达式的名称:
std::cout << typeid(int).name() << std::endl;。
但是这个name()的返回值是。
取决于编译器的
,在vc和gcc中打印出来的结果如下:
int // vc
i // gcc
一个稍微长一点的类型名称,比如:
class Foo {};
std::cout << typeid(Foo*[10]).name() << std::endl;。
打出来是这个效果:
class Foo * [10] // vc。
A10_P3Foo // gcc。
(话说gcc您的返回结果真是。。)
当然了,想在gcc里得到和微软差不多显示效果的方法也是有的,那就是使用。
__cxa_demangle。
:
char* name = abi::__cxa_demangle(typeid(Foo*[10]).name(), nullptr, nullptr, nullptr);。
std::cout << name << std::endl;。
free(name);
显示效果:
Foo* [10]
先不说不同编译器下的适配问题,来看看下面这个会打印出啥:
// vc
std::cout << typeid(const int&).name() << std::endl;。
// gcc
char* name = abi::__cxa_demangle(typeid(const int&).name(), nullptr, nullptr, nullptr);。
std::cout << name << std::endl;。
free(name);
显示效果:
int // vc
int // gcc
可爱的cv限定符和引用都被丢掉了=.=。
如果直接在typeid的结果上加上被丢弃的信息,对于一些类型而言(如函数指针引用)得到的将不是一个正确的类型名称。
想要获得一个类型的完整名称,并且获得的名称必须要是一个正确的类型名称,应该怎样做呢?
一、如何检查C++中的类型
我们需要一个泛型类,用特化/偏特化机制静态检查出C++中的各种类型,并且不能忽略掉类型限定符(type-specifiers)和各种声明符(declarators)。
先来考虑一个最简单的类模板:
template <typename T>。
struct check
// ...
};
假如在它的基础上特化,需要写多少个版本呢?我们可以稍微实现下试试:
template <typename T> struct check<T &>;。
template <typename T> struct check<T const &>;。
template <typename T> struct check<T volatile &>;。
template <typename T> struct check<T const volatile &>;。
template <typename T> struct check<T &&>;。
template <typename T> struct check<T const &&>;。
template <typename T> struct check<T volatile &&>;。
template <typename T> struct check<T const volatile &&>;。
template <typename T> struct check<T *>;。
template <typename T> struct check<T const *>;。
template <typename T> struct check<T volatile *>;。
template <typename T> struct check<T const volatile *>;。
template <typename T> struct check<T * const>;。
template <typename T> struct check<T * volatile>;。
template <typename T> struct check<T * const volatile>;。
template <typename T> struct check<T []>;。
template <typename T> struct check<T const []>;。
template <typename T> struct check<T volatile []>;。
template <typename T> struct check<T const volatile []>;。
template <typename T, size_t N> struct check<T [N]>;。
template <typename T, size_t N> struct check<T const [N]>;。
template <typename T, size_t N> struct check<T volatile [N]>;。
template <typename T, size_t N> struct check<T const volatile [N]>;。
// ......
这还远远没有完。有同学可能会说了,我们不是有伟大的宏嘛,这些东西都像是一个模子刻出来的,弄一个宏批量生成下不就完了。
实际上当我们真的信心满满的动手去写这些宏的时候,才发现适配上的细微差别会让宏写得非常痛苦(比如&和*的差别,[]和[N]的差。
别,还有函数类型、函数指针、函数指针引用、函数指针数组、类成员指针、……)。当我们一一罗列出需要特化的细节时,不由得感叹C++类型系统的复杂和纠。
结。
但是上面的理由并不是这个思路的致命伤。
不可行的地方在于:我们可以写一个多维指针,或多维数组,类型是可以嵌套的。总不可能为每一个维度都特化一个模板吧。
不过正由于类型其实是嵌套的,我们可以用模板元编程的基本思路来搞定这个问题:
template <typename T> struct check<T const> : check<T>;。
template <typename T> struct check<T volatile> : check<T>;。
template <typename T> struct check<T const volatile> : check<T>;。
template <typename T> struct check<T & > : check<T>;。
template <typename T> struct check<T &&> : check<T>;。
template <typename T> struct check<T * > : check<T>;。
// ......
一个简单的继承,就让特化变得simple很多。因为当我们萃取出一个类型,比如T *,之后的T其实是携带上了除*之外所有其他类型信息的一个类型。那么把这个T再重复投入check中,就会继续萃取它的下一个类型特征。
可以先用指针、引用的萃取来看看效果:
#include <iostream>。
template <typename T>。
struct check
check(void) { std::cout << typeid(T).name(); }。
~check(void) { std::cout << std::endl; }。
};
#define CHECK_TYPE__(OPT) \。
template <typename T> \。
struct check<T OPT> : check<T> \。
{ \
check(void) { std::cout << " "#OPT; } \。
};
CHECK_TYPE__(const)。
CHECK_TYPE__(volatile)。
CHECK_TYPE__(const volatile)。
CHECK_TYPE__(&)。
CHECK_TYPE__(&&)。
CHECK_TYPE__(*)。
int main(void)
check<const volatile void * const*&>();。
system("pause");。
return 0;
输出结果(vc):
void const volatile * const * &。
很漂亮,是不是?当然,在gcc里这样输出,void会变成v,所以gcc下面要这样写check模板:
template <typename T>。
struct check
check(void)
{
char* real_name = abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, nullptr);。
std::cout << real_name;。
free(real_name);。
}
~check(void) { std::cout << std::endl; }。
};
二、保存和输出字符串
我们可以简单的这样修改check让它同时支持vc和gcc:
template <typename T>。
struct check
check(void)。
{
# if defined(__GNUC__)。
char* real_name = abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, nullptr);。
std::cout << real_name;。
free(real_name);。
# else
std::cout << typeid(T).name();。
# endif
}
~check(void) { std::cout << std::endl; }。
};
但是到目前为止,check的输出结果都是无法保存的。比较好的方式是可以像typeid(T).name()一样返回一个字符串。这就要求check能够把结果保存在一个std::string对象里。
当然了,我们可以直接给check一个“std::string& 。
out”类型的构造函数,但是这样会把输出的状态管理、字符的打印逻辑等等都揉在一起。因此,比较好的设计方法是实现一个output类,负责输出和维护。
状态。我们到后面就会慢慢感觉到这样做的好处在哪里。
output类的实现可以是这样:
class output
bool is_compact_ = true;。
template <typename T>。
bool check_empty(const T&) { return false; }。
bool check_empty(const char* val)。
{
return (!val) || (val[0] == 0);。
}
template <typename T>。
void out(const T& val)。
{
if (check_empty(val)) return;。
if (!is_compact_) sr_ += " ";。
using ss_t = std::ostringstream;。
sr_ += static_cast<ss_t&>(ss_t() << val).str();。
is_compact_ = false;。
}
std::string& sr_;。
public:
output(std::string& sr) : sr_(sr) {}。
output& operator()(void) { return (*this); }。
template <typename T1, typename... T>。
output& operator()(const T1& val, const T&... args)。
{
out(val);
return operator()(args...);。
}
output& compact(void)。
{
is_compact_ = true;。
return (*this);。
}
};
这个小巧的output类负责自动管理输出状态(是否增加空格)和输出的类型转换(使用std::ostringstream)。
上面的实现里有两个比较有意思的地方。
一是operator()的做法,采用了变参模板。这种做法让我们可以这样用output:
output out(str);。
out("Hello", "World", 123, "!");。
这种写法比cout的流操作符舒服多了。
二是operator()和compact的返回值。当然,这里可以直接使用void,但是这会造成一些限制。
比如说,我们想在使用operator()之后马上compact呢?若让函数返回自身对象的引用,就可以让output用起来非常顺手:
output out(str);。
out.compact()("Hello", "World", 123, "!").compact()("?");。
check的定义和CHECK_TYPE__宏只需要略作修改就可以使用output类:
template <typename T>。
struct check
output out_;
check(const output& out) : out_(out)。
{
# if defined(__GNUC__)。
char* real_name = abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, nullptr);。
out_(real_name);。
free(real_name);。
# else
out_(typeid(T).name());。
# endif
}
};
#define CHECK_TYPE__(OPT) \。
template <typename T> \。
struct check<T OPT> : check<T> \。
{ \
using base_t = check<T>; \。
using base_t::out_; \。
check(const output& out) : base_t(out) { out_(#OPT); } \。
};
为了让外部的使用依旧简洁,实现一个外敷函数模板是很自然的事情:
template <typename T>。
inline std::string check_type(void)。
std::string str;。
check<T> { str };。
return std::move(str);。
int main(void)
std::cout << check_type<const volatile void * const*&>() << std::endl;。
system("pause");。
return 0;
×
loading..
资讯
安全。
论坛。
下载。
读书。
程序开发。
数据库。
系统。
网络。
电子书。
微信学院。
站长学院。
QQ。
手机软件。
考试 。
软件开发|
web前端|
Web开发|
移动开发|
综合编程| 。
首页 > 程序开发 > 软件开发 > C语言 > 正文。
善用backtrace解决大问题。
2011-07-22 。
0 个评论
收藏
我要投稿
一.用途:
主要用于程序异常退出时寻找错误原因。
二.功能:
回溯堆栈,简单的说就是可以列出当前函数调用关系。
三.原理:
1. 通过对当前堆栈的分析,找到其上层函数在栈中的帧地址,再分析上层函数的堆栈,再找再上层的帧地址……一直找到最顶层为止,帧地址指的是一块:在栈上存放局部变量,上层返回地址,及寄存器值的空间。
2.
由于不同处理器堆栈方式不同,此功能的具体实现是编译器的内建函数__buildin_frame_address及。
__buildin_return_address中,它涉及工具glibc和gcc, 。
如果编译器不支持此函数,也可自己实现此函数,举例中有arm上的实现。
四.方法:
在程序中加入backtrace及相关函数调用。
五.举例:
1. 一般backtrace的实现。
i. 程序
#include <signal.h>。
#include <stdio.h>。
#include <stdlib.h>。
#include <execinfo.h>。
#include <sys/types.h>。
#include <sys/stat.h>。
#include <fcntl.h>。
#include <string.h>。
#include <unistd.h>。
#define PRINT_DEBUG。
static void print_reason(int sig, siginfo_t * info, void *secret)。
{
void *array[10];。
size_t size;
#ifdef PRINT_DEBUG。
char **strings;。
size_t i;
size = backtrace(array, 10);。
strings = backtrace_symbols(array, size);。
printf("Obtained %zd stack frames.\n", size);。
for (i = 0; i < size; i++)。
printf("%s\n", strings[i]);。
free(strings);。
#else
int fd = open("err.log", O_CREAT | O_WRONLY);。
size = backtrace(array, 10);。
backtrace_symbols_fd(array, size, fd);。
close(fd);
#endif
exit(0);
}
void die()
{
char *test1;
char *test2;
char *test3;
char *test4 = NULL;。
strcpy(test4, "ab");。
}
void test1()
{
die();
}
int main(int argc, char **argv)。
{
struct sigaction myAction;。
myAction.sa_sigaction = print_reason;。
sigemptyset(&myAction.sa_mask);。
myAction.sa_flags = SA_RESTART | SA_SIGINFO;。
sigaction(SIGSEGV, &myAction, NULL);。
sigaction(SIGUSR1, &myAction, NULL);。
sigaction(SIGFPE, &myAction, NULL);。
sigaction(SIGILL, &myAction, NULL);。
sigaction(SIGBUS, &myAction, NULL);。
sigaction(SIGABRT, &myAction, NULL);。
sigaction(SIGSYS, &myAction, NULL);。
test1();
}
ii. 编译参数
gcc main.c -o test -g -rdynamic。
2. 根据不同的处理器自已实现backtrace。
i. arm的backtrace函数实现。
static int backtrace_xy(void **BUFFER, int SIZE)。
{
volatile int n = 0;。
volatile int *p;。
volatile int *q;。
volatile int ebp1;。
volatile int eip1;。
volatile int i = 0;。
p = &n;
ebp1 = p[4];
eip1 = p[6];
fprintf(stderr, "======================= backtrace_xy addr: 0x%0x, param1: 0x%0x, param2: 0x%0x\n",。
backtrace_xy, &BUFFER, &SIZE);。
fprintf(stderr, "n addr is 0x%0x\n", &n);。
fprintf(stderr, "p addr is 0x%0x\n", &p);。
for (i = 0; i < SIZE; i++)。
{
fprintf(stderr, "ebp1 is 0x%0x, eip1 is 0x%0x\n", ebp1, eip1);。
BUFFER[i] = (void *)eip1;。
p = (int*)ebp1;。
q = p - 5;
eip1 = q[5];
ebp1 = q[2];
if (ebp1 == 0 || eip1 == 0)。
break;
}
fprintf(stderr, "total level: %d\n", i);。
return i;
}
六.举例2:
/*main.c*/
#include "sigsegv.h"。
#include <string.h>。
int die() {
char *err = NULL;。
strcpy(err, "gonner");。
return 0;
}
int main() {
return die();。
}
/*sigsegv.c*/
#define _GNU_SOURCE。
#include <memory.h>。
#include <stdlib.h>。
#include <stdio.h>。
#include <signal.h>。
#include <ucontext.h>。
#include <dlfcn.h>。
#include <execinfo.h>。
#define NO_CPP_DEMANGLE。
#ifndef NO_CPP_DEMANGLE。
#include <cxxabi.h>。
#endif
#if defined(REG_RIP)。
# define SIGSEGV_STACK_IA64。
# define REGFORMAT "%016lx"。
#elif defined(REG_EIP)。
# define SIGSEGV_STACK_X86。
# define REGFORMAT "%08x"。
#else
# define SIGSEGV_STACK_GENERIC。
# define REGFORMAT "%x"。
#endif
static void signal_segv(int signum, siginfo_t* info, void*ptr) {。
static const char *si_codes[3] = {"", "SEGV_MAPERR", "SEGV_ACCERR"};。
size_t i;
ucontext_t *ucontext = (ucontext_t*)ptr;。
#if defined(SIGSEGV_STACK_X86) || defined(SIGSEGV_STACK_IA64)。
int f = 0;。
Dl_info dlinfo;。
void **bp = 0;。
void *ip = 0;。
#else
void *bt[20];。
char **strings;。
size_t sz;。
#endif
fprintf(stderr, "Segmentation Fault!\n");。
fprintf(stderr, "info->si_signo = %d\n", signum);。
fprintf(stderr, "info->si_errno = %d\n", info->si_errno);。
// fprintf(stderr, "info->si_code = %d (%s)\n", info->si_code, info->si_codes[si_code]);。
fprintf(stderr, "info->si_addr = %p\n", info->si_addr);。
for(i = 0; i < NGREG; i++)。
fprintf(stderr, "reg[%02d] = 0x" REGFORMAT "\n", i, ucontext->uc_mcontext.gregs[i]);。
#if defined(SIGSEGV_STACK_X86) || defined(SIGSEGV_STACK_IA64)。
# if defined(SIGSEGV_STACK_IA64)。
ip = (void*)ucontext->uc_mcontext.gregs[REG_RIP];。
bp = (void**)ucontext->uc_mcontext.gregs[REG_RBP];。
# elif defined(SIGSEGV_STACK_X86)。
ip = (void*)ucontext->uc_mcontext.gregs[REG_EIP];。
bp = (void**)ucontext->uc_mcontext.gregs[REG_EBP];。
# endif
fprintf(stderr, "Stack trace:\n");。
while(bp != & ip) {。
if(!dladdr(ip, &dlinfo))。
break;。
const char *symname = dlinfo.dli_sname;。
#ifndef NO_CPP_DEMANGLE。
int status;。
char *tmp = __cxa_demangle(symname, NULL, 0, &status);。
if(status == 0 !=& tmp)。
symname = tmp;。
#endif
fprintf(stderr, "% 2d: %p < %s+%u> (%s)\n",。
++f,。
ip,。
symname,。
(unsigned)(ip - dlinfo.dli_saddr),。
dlinfo.dli_fname);。
#ifndef NO_CPP_DEMANGLE。
if(tmp)。
free(tmp);。
#endif
if(dlinfo.dli_sname != !strcmp(dlinfo.dli_sname, "main"))。
break;。
ip = bp[1];。
bp = (void**)bp[0];。
}
#else
fprintf(stderr, "Stack trace (non-dedicated):\n");。
sz = backtrace(bt, 20);。
strings = backtrace_symbols(bt, sz);。
for(i = 0; i < sz; ++i)。
fprintf(stderr, "%s\n", strings[i]);。
#endif
fprintf(stderr, "End of stack trace\n");。
exit (-1);。
}
int setup_sigsegv() {。
struct sigaction action;。
memset(&action, 0, sizeof(action));。
action.sa_sigaction = signal_segv;。
action.sa_flags = SA_SIGINFO;。
if(sigaction(SIGSEGV, &action, NULL) < 0) {。
perror("sigaction");。
return 0;。
}
return 1;
}
#ifndef SIGSEGV_NO_AUTO_INIT。
static void __attribute((constructor)) init(void)。
{
setup_sigsegv();。
}
#endif
/*sigsegv.h*/
#ifndef __sigsegv_h__。
#define __sigsegv_h__。
#ifdef __cplusplus。
extern "C" {
#endif
int setup_sigsegv();。
#ifdef __cplusplus。
}
#endif
#endif /* __sigsegv_h__ */。
编译时需要加入-rdynamic -ldl –ggdb。
void
handle_signal_error(int rec_signal,siginfo_t* signal_info,void* context)。
{
NE_Info* __attribute__ ((unused)) ne_info = NULL;。
struct sigaction action;。
FILE* file;
void* backtr[NUMBER_OF_BACKTRACE];。
cpal_uns32 __attribute__ ((unused)) i = 0;。
cpal_uns32 backtr_size = 0;。
ucontext_t *u_context;。
time_t seconds_time;。
struct tm* time_struct;。
cpal_si32 ret_t;。
char filename[SIZE_OF_FILENAME]; 。
if(g_handler_running)。
return;
g_handler_running = CPAL_TRUE;。
ret_t = time(&seconds_time); 。
if(ret_t != - 1)。
{
time_struct = gmtime(&seconds_time);。
snprintf(filename,SIZE_OF_FILENAME,"%s%d%d%d-%d%d%d-%s",BACKTRACE_FILE_PATH,time_struct->tm_mon,time_struct->tm_mday,。
(time_struct->tm_year-100)+2000,time_struct->tm_hour,time_struct->tm_min,time_struct->tm_sec,BACKTRACE_FILE);。
}
else
{
snprintf(filename,SIZE_OF_FILENAME,"%s",BACKTRACE_FILE);。
}
file = fopen(filename,"w");。
if(file == NULL)。
{
return;
}
if(signal_info == NULL)。
{
return;
}
if(context == NULL)。
{
return;
}
u_context = (ucontext_t*)context;。
/*Restore the default action for this signal and re-raise it, so that the default action occurs. */。
action.sa_sigaction = SIG_DFL;。
sigemptyset(&action.sa_mask);。
action.sa_flags = SA_RESTART;。
sigaction(rec_signal,&action,NULL);。
/* Print out the backtrace. */。
backtr_size = backtrace(backtr,20);。
/* The backtrace points to sigaction in libc, not to where the signal was actually raised.。
This overwrites the sigaction with where the signal was sent, so we can resolve the sender. */。
#if __WORDSIZE == 64。
backtr[1] = (void*)u_context->uc_mcontext.gregs[REG_RIP];。
#else
backtr[1] = (void*)u_context->uc_mcontext.gregs[REG_EIP];。
#endif //__WORDSIZE。
backtrace_symbols_fd(backtr,backtr_size,fileno(file));。
fprintf(file,"Backtrace is above.\nFatal signal %d received.\n",rec_signal);。
#if __WORDSIZE == 64。
fprintf(file,"Signal received at address %p from 0x%08x.\n",signal_info->si_addr,。
u_context->uc_mcontext.gregs[REG_RIP]);。
#else
fprintf(file,"Signal received at address %p from 0x%08x.\n",signal_info->si_addr,。
u_context->uc_mcontext.gregs[REG_EIP]);。
#endif //__WORDSIZE。
#if CPAL_LM_DEBUG。
/* Print all NE_Infos */。
for(; i < MAX_NO_OF_CONNS; i++)。
{
ne_info = g_ne_hash_tab[i];。
while(ne_info != NULL)。
{
ne_info = ne_info->next_ne;。
}
}
#endif
fflush(file);
fclose(file);
sleep (50); /* Sleep for 50 seconds */。
g_handler_running = *_FALSE;。
raise(rec_signal);。
}
gcc就是那样的,只输出类型名的第一个字符,要输出完整的名字可以这样:
#include <iostream>。
#include <typeinfo>。
#include <cxxabi.h> //使用abi 。
using namespace std;。
int main()
cout<<abi::__cxa_demangle(typeid(int).name(),0,0,0 )<<endl;。
return 0;
第一步:在终端运行Java程序。
第二步:通过命令 pidof java 找到已经启动的java进程的ID,选择需要查看的java程序的进程ID。
第三步:使用命令 kill -3 <java进行的 pid> 打印出java程序的线程堆栈信息。
第四步:通常情况下运行的项目可能会比较大,那么这个时候打印的堆栈信息可能会有几千到几万行,为了方便查看,我们往往需要将输出内容进行重定向。
使用linux下的重定向命令方式即可:例如: demo.sh > run.log 2>&1 将输出信息重定向到 run.log中。
注:在操作系统中,0 1 2分别对应着不同的含义, 如下:
0 : 标准输入,即:C中的stdin , java中的System.in。
1 : 标准输出, 即:C中的stdout ,java中的System.out。
2 : 错误输出, 即:C中的stderr , java中的System.err。
Demo:
----------------------------------------------------------------------------------------------。
Sources Code :
public class PrintThreadTrace {。
Object obj1 = new Object();。
Object obj2 = new Object();。
public void func1(){。
synchronized (obj1){。
func2();。
}
}
public void func2(){。
synchronized (obj2){。
while(true){。
System.out.print("");。
}
}
}
public static void main(String[] args){。
PrintThreadTrace ptt = new PrintThreadTrace();。
ptt.func1();。
}
}
----------------------------------------------------------------------------------------------------------------。
按照步骤操作后的打印输出信息:
Full thread dump Java HotSpot(TM) 64-Bit Server VM (24.79-b02 mixed mode):。
"Service Thread" daemon prio=10 tid=0x00007fdc880a9000 nid=0x12a4 runnable [0x0000000000000000]。
java.lang.Thread.State: RUNNABLE。
"C2 CompilerThread1" daemon prio=10 tid=0x00007fdc880a7000 nid=0x12a3 waiting on condition [0x0000000000000000]。
java.lang.Thread.State: RUNNABLE。
"C2 CompilerThread0" daemon prio=10 tid=0x00007fdc880a4000 nid=0x12a2 waiting on condition [0x0000000000000000]。
java.lang.Thread.State: RUNNABLE。
"JDWP Command Reader" daemon prio=10 tid=0x00007fdc50001000 nid=0x1299 runnable [0x0000000000000000]。
java.lang.Thread.State: RUNNABLE。
"JDWP Event Helper Thread" daemon prio=10 tid=0x00007fdc880a1800 nid=0x1298 runnable [0x0000000000000000]。
java.lang.Thread.State: RUNNABLE。
"JDWP Transport Listener: dt_socket" daemon prio=10 tid=0x00007fdc8809e000 nid=0x1297 runnable [0x0000000000000000]。
java.lang.Thread.State: RUNNABLE。
"Signal Dispatcher" daemon prio=10 tid=0x00007fdc88091000 nid=0x1296 waiting on condition [0x0000000000000000]。
java.lang.Thread.State: RUNNABLE。
"Finalizer" daemon prio=10 tid=0x00007fdc88071800 nid=0x1295 in Object.wait() [0x00007fdc77ffe000]。
java.lang.Thread.State: WAITING (on object monitor)。
at java.lang.Object.wait(Native Method)。
- waiting on <0x00000000ecb04858> (a java.lang.ref.ReferenceQueue$Lock)。
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)。
- locked <0x00000000ecb04858> (a java.lang.ref.ReferenceQueue$Lock)。
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)。
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)。
"Reference Handler" daemon prio=10 tid=0x00007fdc8806f800 nid=0x1294 in Object.wait() [0x00007fdc7c10b000]。
java.lang.Thread.State: WAITING (on object monitor)。
at java.lang.Object.wait(Native Method)。
- waiting on <0x00000000ecb04470> (a java.lang.ref.Reference$Lock)。
at java.lang.Object.wait(Object.java:503)。
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)。
- locked <0x00000000ecb04470> (a java.lang.ref.Reference$Lock)。
"main" prio=10 tid=0x00007fdc8800b800 nid=0x128e runnable [0x00007fdc8fef7000]。
java.lang.Thread.State: RUNNABLE。
at com.wenchain.study.PrintThreadTrace.func2(PrintThreadTrace.java:20)。
- locked <0x00000000ecc04b20> (a java.lang.Object)。
at com.wenchain.study.PrintThreadTrace.func1(PrintThreadTrace.java:13)。
- locked <0x00000000ecc04b10> (a java.lang.Object)。
at com.wenchain.study.PrintThreadTrace.main(PrintThreadTrace.java:27)。
"VM Thread" prio=10 tid=0x00007fdc8806b000 nid=0x1293 runnable 。
"GC task thread#0 (ParallelGC)" prio=10 tid=0x00007fdc88021000 nid=0x128f runnable 。
"GC task thread#1 (ParallelGC)" prio=10 tid=0x00007fdc88023000 nid=0x1290 runnable 。
"GC task thread#2 (ParallelGC)" prio=10 tid=0x00007fdc88024800 nid=0x1291 runnable 。
"GC task thread#3 (ParallelGC)" prio=10 tid=0x00007fdc88026800 nid=0x1292 runnable 。
"VM Periodic Task Thread" prio=10 tid=0x00007fdc880b3800 nid=0x12a5 waiting on condition 。
JNI global references: 1391。
Heap
PSYoungGen total 17920K, used 1270K [0x00000000ecb00000, 0x00000000ede80000, 0x0000000100000000)。
eden space 15872K, 8% used [0x00000000ecb00000,0x00000000ecc3d898,0x00000000eda80000)。
from space 2048K, 0% used [0x00000000edc80000,0x00000000edc80000,0x00000000ede80000)。
to space 2048K, 0% used [0x00000000eda80000,0x00000000eda80000,0x00000000edc80000)。
ParOldGen total 39424K, used 0K [0x00000000c6200000, 0x00000000c8880000, 0x00000000ecb00000)。
object space 39424K, 0% used [0x00000000c6200000,0x00000000c6200000,0x00000000c8880000)。
PSPermGen total 21504K, used 2619K [0x00000000c1000000, 0x00000000c2500000, 0x00000000c6200000)。
object space 21504K, 12% used [0x00000000c1000000,0x00000000c128edd8,0x00000000c2500000)。
----------------------------------------------------------------------------------------------------------------------------。
上面的信息中包含了当前JVM中所有运行的线程信息,其中在示例中我们启动的线程为main线程,其余的都是JVM自己创建的。
在打印的信息中,我们可以清楚的看见当前线程的调用上下文,可以很清楚的知道程序的运行情况。
并且我们在最后面还能看见当前虚拟机中的内存使用情况,青年世代,老年世代的信息等等...。
PS: 在JDK1.5以上,我们可以通过在Java程序中调用Thread.getStackTrace()方法来进行堆栈的自动打印,使得线程堆栈的打印时机可编程控制。
文章知识点与官方知识档案匹配
Java技能树首页概览
89841 人正在系统学习中
点击阅读全文
打开CSDN,阅读体验更佳
jstack-查看Java进程的线程堆栈信息,锁定高消耗资源代码。
jstack主要用来查看某个Java进程内的线程堆栈信息。语法格式如下: jstack[option]pid jstack[option]executable core jstack[option][server-id@]remote-hostname-or-ip 命令行参数选项说明如下: ...。
011Java并发包018查看线程堆栈信息_执笔未来的博客。
java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1088) java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809) java.util.concurre...。
最新发布 jstack -- java堆栈常用排查指令。
jstack -- java堆栈常用排查指令。
继续访问
热门推荐 jstack 命令查看JAVA线程堆栈。
JAVA堆栈信息实际生产中,可能由于开发以及测试未能全面覆盖的代码质量、性能问题,而引致线程挂起甚至崩溃。可能就需要查看堆栈信息来排查问题了。jps -lvmjps -lvm 用于查看当前机器上运行的java进程。C:\Users\Administrator>jps -lvm 7348 -Dosgi.requiredJavaVersion=1.8 -Dosgi.instance.area.defa。
继续访问
Java多线程——查看线程堆栈信息。
Java多线程——查看线程堆栈信息 摘要:本文主要介绍了查看线程堆栈信息的方法。 使用Thread类的getAllStackTraces()方法 方法定义 可以看到getAllStackTraces()方法的返回值是一个Map对象,key是Thread的实例,value是一个StackTraceElement实例数组: 1 public static Map<Thread, S...。
继续访问
java堆栈常用排查指令
java 异常排查四板斧 1、查看java 堆栈线程信息 说明 jstack命令打印指定Java进程、核心文件或远程调试服务器的Java线程的Java堆栈跟踪信息。 对于每个Java框架,完整的类名,方法名, 字节码索引(BCI)和行号(如果有的话)被打印出来。 使用-m选项,jstack命令打印程序中所有线程的Java和本机帧 计数器(PC)。 对于每个本机帧,当可用时,将打印离PC最近的本机符号。 c++乱码的名字不会被修改。 要demangle c++名称,输出这个 命令可以管道到c++filt。 当。
继续访问
java诊断工具-Arthas(thread命令)查看当前线程堆栈。
cpu使用率与linux 命令top -H -p <pid>的线程CPU类似 1、支持一键展示当前最忙的前N个线程并打印堆栈 thread -n 3 没有线程ID,包含[Internal]表示为JVM内部线程,参考dashboard命令的介绍。 cpuUsage为采样间隔时间内线程的CPU使用率,与dashboard命令的数据一致。 deltaTime为采样间隔时间内线程的增量CPU时间,小于1ms时被取整显示为0ms。 time线程运行总CPU...。
继续访问
java查看线程的堆栈信息
通过使用jps 命令获取需要监控的进程的pid,然后使用jstackpid 命令查看线程的堆栈信息。 通过jstack命令可以获取当前进程的所有线程信息。 每个线程堆中信息中,都可以查看到线程ID、线程的状态(wait、sleep、running 等状态)、是否持有锁信息等。 jstack -l <pid> >jvm_listlocks.txt 转...。
继续访问
java 查看线程堆栈信息_Java多线程——查看线程堆栈信息。
java多线程——查看线程堆栈信息摘要:本文主要介绍了查看线程堆栈信息的方法。使用thread类的getallstacktraces()方法方法定义可以看到getallstacktraces()方法的返回值是一个map对象,key是thread的实例,value是一个stacktraceelement实例数组:1 public static map getallstacktraces()使用可以使...。
继续访问
java线程堆栈信息分析
java堆栈信息分析
继续访问
java 查看堆栈_javap 命令查看堆栈中信息。
javap命令是对.java文件进行反编译,通过这个命令可以看到堆栈中是怎么压栈和出栈的已经执行顺序,这里简单解释下javap的简单的使用,下面举个例子:题目:i++ 和++i的区别解释:简单点说 这个问题都不难回答,这里就不说了,但是实际上堆栈中区别也是老大了(这里就用到了javap命令), 步骤:1.在任意一个盘下面建一个名为Test.java的文件(文件名可以随意命名)代码如下:public...。
继续访问
java 查看线程堆栈信息_jstack-查看Java进程的线程堆栈信息,锁定高消耗资源代码。...。
jstack主要用来查看某个Java进程内的线程堆栈信息。语法格式如下:jstack[option]pidjstack[option]executablecorejstack[option][server-id@]remote-hostname-or-ip命令行参数选项说明如下:-llonglistings,会打印出额外的锁信息,在发生死锁时可以用jstack-lpid来观察...。
继续访问
java堆栈信息怎么看_线程堆栈信息怎么看? - cs_person的个人空间 - OSCHINA - 中文开源技术交流社区...。
一条线程堆栈信息大概长成下面这个样子:RMI TCP Connection(267865)-172.16.5.25" daemon prio=10 tid=0x00007fd508371000 nid=0x55ae waiting for monitor entry [0x00007fd4f8684000]java.lang.Thread.State: BLOCKED (on object m...。
继续访问
线程堆栈信息怎么看?
一条线程堆栈信息大概长成下面这个样子: RMI TCP Connection(267865)-172.16.5.25" daemon prio=10 tid=0x00007fd508371000 nid=0x55ae waiting for monitor entry [0x00007fd...。
继续访问
java的栈和堆
栈与堆都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。 Java 的堆是一个运行时数据区,类的(对象从中分配空间。这些对象通过new、newarray、anewarray和multianewarray等指令建立,它们不需要程序代码来显式的释放。堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在...。
继续访问
查看java线程_【JAVA】Java线程堆栈信息查看。
如何获得线程的堆栈信息?线上服务器cpu 100%了,该如何排查问题?1.top命令查询哪个pid进程占用cpu高(ps -ef|grep java 获取PID号)2.通过 top -Hp pid 可以查看该进程下各个线程的cpu使用情况,获取占用cpu高的线程id3.执行命令:printf "%X\n" 线程tid(用于获取占用cpu高的线程id的16进制数)4.执行命令:jstack pid ...。
继续访问
kill -3 java_kill -3 PID命令获取java应用堆栈信息。
一、应用场景:当linux服务器出现异常情况(响应缓慢,负载持续飙升)并且服务器没有安装对应的包而无法使用jstack等命令时,可以使用linux的kill相关命令打印堆栈信息。命令格式:kill -3 PID二、执行步骤:2.1、获取java进程的PIDps -ef|grep java结果的第二列数字就是进程对应的pid。2.2、kill -3 PID(1)如果项目通过Tomcat进行发布(普通...。
继续访问
jstack 工具 查看JVM堆栈信息。
1|0介绍 jstack是java虚拟机自带的一种堆栈跟踪工具。jstack用于打印出给定的java进程ID或corefile或远程调试服务的Java堆栈信息,如果是在64位机器上,需要指定选项"-J-d64",Windows的jstack使用方式只支持以下的这种方式: jstack [-l] pid 主要分为两个功能: a. 针对活着的进程做本地的或远程的线程dump; b. 针对core文件做线程dump。 jstack用于生成java虚拟机当前时刻的线程快照。线程快照是...。
继续访问
linux查看java堆栈
1、查看JAVA进程JVM参数 jinfo -flags pid(进程号) -XX:CICompilerCount=2 最大的并行编译数 -XX:InitialHeapSize=16777216 JVM 的初始堆内存大小 -XX:MaxHeapSize=257949696 JVM 的最大堆内存大小 -XX:MaxNewSize=85983232 -XX:MinHeapDeltaBytes=196608 -XX:NewSize=5570560 -XX:OldSize=11206656 2、JVM 查看.。
继续访问
Linux 如何查看一个进程的堆栈。
有两种方法:第一种:pstack 进程ID第二种,使用gdb 然后attach 进程ID,然后再使用命令 thread apply all bt 两种方法都可以列出进程所有的线程的当前的调用栈。不过,使用gdb的方法,还可以查看某些信息,例如局部变量,指针等。不过,如果只看调用栈的话,pstack还是很方便的。
继续访问
JAVA获取堆栈信息
1. 通过Throwable获取 StackTraceElement[] stackTrace = new Throwable().getStackTrace(); 2. 通过Thread获取 StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();。
继续访问
java 查看线程栈大小_基于 Java 线程栈的问题排查。
除日志外,还有没有别的方式跟踪线上服务问题呢?或者,跟踪并排除日志里无法发现的问题?方法当然是有的,就是通过现场快照定位并发现问题。我们所说的现场,主要指这两方面:Java 线程栈。线程栈是Java线程工作的快照,可以获得当前线程在做什么;Java 内存堆。堆是JVM的内存快照,可以获取内存分配相关信息。