DEBUG STACK TRACE for PoolBackedDataSource.close().

本文探讨了Spring框架中配置数据源时遇到的关闭异常问题。通过分析代码和配置文件,详细解释了如何正确配置数据源以避免此类错误,并提出了删除不确定的destory-method配置作为解决方案。

1.出现这种情况,我自己的总结:

1).最简单的问题。看看你给了没有get、set 方法。如果你没有提供这两个方法的话。是肯定空指针 而且还报这个错。

2)以下是转载的.自己留个备份。大家也可以看下

先贴出来异常的部分代码:
 
Java代码  
java.lang.Exception: DEBUG STACK TRACE for PoolBackedDataSource.close().  
    at com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource.close(AbstractPoolBackedDataSource.java:417)  
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)  
    ......  
java.lang.Exception: DEBUG -- CLOSE BY CLIENT STACK TRACE  
    at com.mchange.v2.c3p0.impl.NewPooledConnection.close(NewPooledConnection.java:566)  
    at com.mchange.v2.c3p0.impl.NewPooledConnection.close(NewPooledConnection.java:234)  
    .....  
 
 
 
具体到这个问题里,就是来自Spring关于数据源的那个配置文件。
 
Xml代码  
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">  
//略去数据源相关信息的配置  
</bean>  
 
 
 
      Spring在读取这个配置文件以后,需要根据这些信息来实例化一些类,然后内部再根据中间的那些配置信息来实际构造数据源。
      可是来了个问题。不能保证这里的ComboPooledDataSource数据源一定是可用的,也不能保证close方法一定能关闭连接,对吧?Spring本身不能检查这个类是否 真实有效,毫无Bug。实际上呢,也检查不了。同样的,close方法是否有效,也需要进行检查。
 
 
Java代码  
java.sql.Connection  
public interface Connection extends Wrapper  
 
任何一个JDBC数据库连接的实现类都应该实现这个接口的全部方法。比如,close。API里的描述是,立即释放此 Connection 对象的数据库和 JDBC 资源,而不是等待它们被自动释放。
 
 
     虽然API规定了close是关闭连接释放资源的。但这只是你接口的一厢情愿。也许人家实现厂家觉得close方法不够帅,要改成closeConnection。那。。。Spring总不好傻傻的去死扣close方法来关闭连接吧?虽然这方法必须实现,但是可没说一定要有内容啊。如果是空方法呢?所以有了destroy-method这个配置项的出现。
 
  总结:
     当不能确定destory-method的情况下,把该项删除,由程序自主选择关闭方法,这样Debug就不会报错了.

#if BACKWARD_HAS_BFD == 1 template <> class TraceResolverLinuxImpl<trace_resolver_tag::libbfd>: public TraceResolverLinuxImplBase { public: TraceResolverLinuxImpl(): _bfd_loaded(false) {} template <class ST> void load_stacktrace(ST&) {} ResolvedTrace resolve(ResolvedTrace trace) { Dl_info symbol_info; // trace.addr is a virtual address in memory pointing to some code. // Let's try to find from which loaded object it comes from. // The loaded object can be yourself btw. if (!dladdr(trace.addr, &symbol_info)) { return trace; // dat broken trace... } // Now we get in symbol_info: // .dli_fname: // pathname of the shared object that contains the address. // .dli_fbase: // where the object is loaded in memory. // .dli_sname: // the name of the nearest symbol to trace.addr, we expect a // function name. // .dli_saddr: // the exact address corresponding to .dli_sname. if (symbol_info.dli_sname) { trace.object_function = demangle(symbol_info.dli_sname); } if (!symbol_info.dli_fname) { return trace; } trace.object_filename = symbol_info.dli_fname; bfd_fileobject& fobj = load_object_with_bfd(symbol_info.dli_fname); if (!fobj.handle) { return trace; // sad, we couldn't load the object :( } find_sym_result* details_selected; // to be filled. // trace.addr is the next instruction to be executed after returning // from the nested stack frame. In C++ this usually relate to the next // statement right after the function call that leaded to a new stack // frame. This is not usually what you want to see when printing out a // stacktrace... find_sym_result details_call_site = find_symbol_details(fobj, trace.addr, symbol_info.dli_fbase); details_selected = &details_call_site; #if BACKWARD_HAS_UNWIND == 0 // ...this is why we also try to resolve the symbol that is right // before the return address. If we are lucky enough, we will get the // line of the function that was called. But if the code is optimized, // we might get something absolutely not related since the compiler // can reschedule the return address with inline functions and // tail-call optimisation (among other things that I don't even know // or cannot even dream about with my tiny limited brain). find_sym_result details_adjusted_call_site = find_symbol_details(fobj, (void*) (uintptr_t(trace.addr) - 1), symbol_info.dli_fbase); // In debug mode, we should always get the right thing(TM). if (details_call_site.found && details_adjusted_call_site.found) { // Ok, we assume that details_adjusted_call_site is a better estimation. details_selected = &details_adjusted_call_site; trace.addr = (void*) (uintptr_t(trace.addr) - 1); } if (details_selected == &details_call_site && details_call_site.found) { // we have to re-resolve the symbol in order to reset some // internal state in BFD... so we can call backtrace_inliners // thereafter... details_call_site = find_symbol_details(fobj, trace.addr, symbol_info.dli_fbase); } #endif // BACKWARD_HAS_UNWIND if (details_selected->found) { if (details_selected->filename) { trace.source.filename = details_selected->filename; } trace.source.line = details_selected->line; if (details_selected->funcname) { // this time we get the name of the function where the code is // located, instead of the function were the address is // located. In short, if the code was inlined, we get the // function correspoding to the code. Else we already got in // trace.function. trace.source.function = demangle(details_selected->funcname); if (!symbol_info.dli_sname) { // for the case dladdr failed to find the symbol name of // the function, we might as well try to put something // here. trace.object_function = trace.source.function; } } // Maybe the source of the trace got inlined inside the function // (trace.source.function). Let's see if we can get all the inlined // calls along the way up to the initial call site. trace.inliners = backtrace_inliners(fobj, *details_selected); #if 0 if (trace.inliners.size() == 0) { // Maybe the trace was not inlined... or maybe it was and we // are lacking the debug information. Let's try to make the // world better and see if we can get the line number of the // function (trace.source.function) now. // // We will get the location of where the function start (to be // exact: the first instruction that really start the // function), not where the name of the function is defined. // This can be quite far away from the name of the function // btw. // // If the source of the function is the same as the source of // the trace, we cannot say if the trace was really inlined or // not. However, if the filename of the source is different // between the function and the trace... we can declare it as // an inliner. This is not 100% accurate, but better than // nothing. if (symbol_info.dli_saddr) { find_sym_result details = find_symbol_details(fobj, symbol_info.dli_saddr, symbol_info.dli_fbase); if (details.found) { ResolvedTrace::SourceLoc diy_inliner; diy_inliner.line = details.line; if (details.filename) { diy_inliner.filename = details.filename; } if (details.funcname) { diy_inliner.function = demangle(details.funcname); } else { diy_inliner.function = trace.source.function; } if (diy_inliner != trace.source) { trace.inliners.push_back(diy_inliner); } } } } #endif } return trace; } 详细注释上述代码并保留原有注释内容
09-29
`debug.Stack()` 是 Go 标准库中的一个函数,用于 **获取当前 goroutine 的调用栈(stack trace)的原始快照**。它常用于调试、错误追踪和 panic 恢复场景。 --- ### ✅ 函数定义 ```go import "runtime/debug" func Stack() []byte ``` - **返回值类型:`[]byte`** - 内容是当前 goroutine 的完整调用堆栈信息(包括文件名、行号、函数名、调用层级等) - 即使没有发生 `panic` 也可以调用 --- ### 📌 示例代码 ```go package main import ( "fmt" "runtime/debug" ) func a() { b() } func b() { c() } func c() { fmt.Printf("%s", debug.Stack()) } func main() { a() } ``` #### 🔍 输出示例: ```text goroutine 1 [running]: runtime/debug.Stack() /usr/local/go/src/runtime/debug/stack.go:24 +0x65 main.c() /path/to/main.go:13 +0x25 main.b() /path/to/main.go:9 +0x25 main.a() /path/to/main.go:5 +0x25 main.main() /path/to/main.go:17 +0x25 ``` 这显示了从 `main()` 到 `c()` 的完整调用路径。 --- ## ✅ 主要用途 ### 1. **在不 panic 的情况下打印调用栈(用于调试)** ```go fmt.Println("当前调用栈:") fmt.Printf("%s\n", debug.Stack()) ``` 适用于: - 调试某个函数被谁调用了 - 分析执行流程 - 定位“奇怪”的控制流 --- ### 2. **配合 `recover()` 捕获 panic 并记录详细堆栈** ```go func safeCall() { defer func() { if r := recover(); r != nil { fmt.Printf("程序崩溃: %v\n", r) fmt.Printf("堆栈跟踪:\n%s\n", debug.Stack()) } }() panic("测试崩溃") } ``` > ⚠️ 注意:`runtime.Stack()` 和 `debug.Stack()` 是同一个函数的不同导入方式(后者更常用) --- ### 3. **日志中记录异常行为(非致命错误)** 比如你发现某个函数被非法调用: ```go if someInvalidCondition { log.Printf("非法调用 detected:\n%s", debug.Stack()) } ``` 比单纯打一句 `"invalid call"` 强大得多。 --- ## ❗️与 `panic` 堆栈的区别 | 场景 | 输出内容 | |------|--------| | 发生 `panic` 且未 `recover` | 自动打印 stack trace 并退出 | | 使用 `debug.Stack()` | 手动获取 stack trace,程序继续运行 | 👉 所以 `debug.Stack()` 是一种 **主动诊断工具**,而不是被动崩溃报告。 --- ## ✅ 与其他类似函数对比 | 函数 | 说明 | 是否需要 panic | |------|------|---------------| | `debug.Stack()` | 获取当前 goroutine 完整调用栈 | 否 | | `runtime.Caller()` / `Callers()` | 获取某一层的调用信息(如文件、行号) | 否 | | `panic()` + `recover()` | 捕获异常并可结合 `debug.Stack()` 输出 | 是(触发 panic) | --- ## 💡 实际应用场景举例 ### 场景:检测资源泄漏(如未关闭的连接) ```go type Connection struct { createdStack []byte // 记录创建时的堆栈 } func NewConnection() *Connection { return &Connection{ createdStack: debug.Stack(), // 快照创建位置 } } func (c *Connection) Close() { // 正常关闭 } // 在 GC 时检查是否忘记关闭 func (c *Connection) Finalize() { if c.createdStack != nil { log.Printf("警告:Connection 未显式关闭!创建于:\n%s", c.createdStack) } } ``` --- ##
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值